Loading

JVM 垃圾收集

以下知识大部分来自于

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

 

posted @ 2017-07-05 18:02  注销111  阅读(159)  评论(0编辑  收藏  举报