以下知识大部分来自于
http://www.importnew.com/1993.html
(红色部分表示需要确认,需要思考,需要考证,或者我还不懂的地方)
什么是Minor GC
年轻代GC
什么是年轻代
Eden 和 两个Survivor
Minor GC 发生的时机
Eden满了
Minor GC 是一个什么样的过程
Eden满了的时候,Eden中存货的对象被转移到其中一个Survivor空间,每次Eden满了的时候,都会这样做,直到当这个Survivor也满了的时候,会把该Survivor中依然存货的对象转移到另外一个Survivor,然后清空之前的Survivor
什么是老年代
Minor GC多少之后,依然存活于Survivor的对象会被转移到老年代
什么是Card Table
我的理解就是为了加速MinorGC而出现的东西。minor GC发生的时候,势必不会清理掉被老年代引用的对象,但是老年代内存区较大,每次扫描一遍比较花费时间,为了解决这个问题,老年代中存在一个叫card table的东西,它是一个512 Byte大小的块,每个块对应4KB大小的内存空间,如果某一位为1,则表示该位对应的那一个4KB内存是存在老年代指向年轻代引用的,512Byte x 4KB = 16G ,所以我自己想,这应该是虚拟机能管理的堆内存的上限,但card table 和 对应的内存空间是如何对应的呢
什么是bump-the-pointer和TLABs(Thread-Local Allocation Buffers)
HotSpot虚拟机使用了两种技术来加快内存分配。他们分别是是”bump-the-pointer“和“TLABs(Thread-Local Allocation Buffers)”。
Bump-the-pointer技术跟踪在伊甸园空间创建的最后一个对象。这个对象会被放在伊甸园空间的顶部。如果之后再需要创建对象,只需要检查伊甸园空间是否有足够的剩余空间。如果有足够的空间,对象就会被创建在伊甸园空间,并且被放置在顶部。这样以来,每次创建新的对象时,只需要检查最后被创建的对象。这将极大地加快内存分配速度。但是,如果我们在多线程的情况下,事情将截然不同。如果想要线程安全的方式以多线程在伊甸园空间存储对象,不可避免的需要加锁,而这将极大地的影响性能。TLABs 是HotSpot虚拟机针对这一问题的解决方案。该方案为每一个线程在伊甸园空间分配一块独享的空间,这样每个线程只访问他们自己的TLAB空间,再与bump-the-pointer技术结合可以在不加锁的情况下分配内存。
Major GC 发生的时机
老年代空间的GC事件基本上是在空间已满时发生,执行的过程根据GC类型不同而不同
如果对象间有循环引用,GC是如何处理的
谈谈你对几种GC算法的理解
一共有5种GC算法:
1.Serial GC
2.Parallel GC
3.Parallel GC
4.Concurrent Mark&Sweep GC (CMS GC)
5.G1 GC
GC Roots 根节点有哪些
从可达性分析中GC Roots节点找引用链这个操作为例,可作为的节点主要在全局性的引用(例如常量或类静态属性)与执行上下文(例如栈帧中的本地变量表)中
GC日志分析
先在虚拟机参数中加上 -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails
然后执行下面的代码
package com.test.jvm.gc;/** * @contact 408657544@qq.com * @date 2017年9月22日 * @Description: testGC()方法执行后,objA和objB会不会被GC呢? */public class ReferenceCountingGC { public Object instance = null; private static final int _1MB = 1024*1024; /** 这个成员属性的唯一意义就是占点内存,以便能在GC日志中看清楚是否被回收过 */ private byte[] bigSize = new byte[2*_1MB]; public static void testGC() { ReferenceCountingGC objA = new ReferenceCountingGC(); ReferenceCountingGC objB = new ReferenceCountingGC(); objA.instance = objB; objB.instance = objA; objA = null; objB = null; //假设在这行发生GC,objA和objB是否能被回收? System.gc(); } public static void main(String[] args) { testGC(); }}
输出:
[GC (System.gc()) [PSYoungGen: 5223K->480K(6144K)] 5223K->648K(19968K), 0.0008552 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (System.gc()) [PSYoungGen: 480K->0K(6144K)] [ParOldGen: 168K->609K(13824K)] 648K->609K(19968K), [Metaspace: 2692K->2692K(1056768K)], 0.0047749 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] Heap PSYoungGen total 6144K, used 56K [0x00000000ff980000, 0x0000000100000000, 0x0000000100000000) eden space 5632K, 1% used [0x00000000ff980000,0x00000000ff98e2b8,0x00000000fff00000) from space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000) to space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000) ParOldGen total 13824K, used 609K [0x00000000fec00000, 0x00000000ff980000, 0x00000000ff980000) object space 13824K, 4% used [0x00000000fec00000,0x00000000fec98448,0x00000000ff980000) Metaspace used 2699K, capacity 4486K, committed 4864K, reserved 1056768K class space used 286K, capacity 386K, committed 512K, reserved 1048576K
=====================以下是杂乱的思考==========================
jdk8中已经移除了永生代这个东东,数据转移到了metaspace area。
新创建的对象都放在eden, eden满了之后 jvm执行mark-copy算法,标记那些还幸存的对象,放到survivor1,其他的都清理掉。
等下一次eden或者survivor1满的时候,mark-copy又开始工作,将eden和survivor1中幸存的对象标记一下,放到survivor2,其他的都清理掉
以上就是年轻代的垃圾回收,成为minor GC
老年代比较复杂,老年代的垃圾回收成为major GC