《理解Java虚拟机》第三章读书笔记:垃圾收集器与内存分配

本文深入探讨了Java垃圾回收的基本概念、算法、过程及其在实际应用中的具体操作,包括对象的引用计数、根搜索算法、垃圾收集器的工作原理、内存分配与回收策略,以及如何利用这些知识优化内存管理。

1. 概述

GC需要完成的事情
1、哪些内存需要回收
2、什么时候回收
3、如何回收

2. 对象已死

2.1 引用计数算法

给对象中添加一个引用计算器,每当有一个地方引用它时,计数器加一,当引用失效时,计数器减一
。任何时刻都为0的对象就是不可能再被使用了。

2.2 根搜索算法

通过一系列的名为“GC Root”的对象起始点,从这些节点开始向下搜索,搜索所有走过的路径称为引用链。当一个对象到“GC Root”没有任何引用链相连。则证明对象不可用。

GC root对象包括如下
虚拟栈(栈中的引用变量表)中的引用的对象
方法区中的类静态属性引用的对象
方法区的常量引用的对象
本地方法栈中的JNI(native)方法

2.3 再谈引用

强引用:只要强引用存在,垃圾回收器永远不会回收掉被原因的对象
软引用:在系统发将要发生内存溢出溢出之前,将会把这些对象列进回收范围之中进行第二次回收
弱引用:被关联的对象只能生存到下一次垃圾收集发生之前
虚引用:一个对象是否 有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚yiny取得一个对象实例

2.4 存在还是死亡

对象的死亡至少经过两次标记
第一次:对象从根搜索发现没有与GC Roots相连接的引用链,那它会被第一次标记并且进行一次筛选,筛选条件为对象是否有必要执行finalize()。
第二次:对象执行了finalize()方法,那么这个对象会被放置一个名为F-Queue的队列之中。
代码清单:

package com.one.jvm;

/**
 * 测试对象只调用一次Finalize()方法
 * @author Administrator
 *
 */
public class FinalizeEscapeGC {


    public static FinalizeEscapeGC escapeGC = null;


    @Override
    protected void finalize() throws Throwable {
        // TODO Auto-generated method stub
        super.finalize();
        System.out.println("finalize method excuted");
        escapeGC = this;
    }

    public static void main(String[] args) throws InterruptedException {
        escapeGC = new FinalizeEscapeGC();

        escapeGC = null;
        //运行垃圾回收器。
        System.gc();
        Thread.sleep(600);

        if(escapeGC !=null) {
            System.out.println("escapeGC Object not  null");
        } else {
            System.out.println("escapeGC Object is null");
        }


        escapeGC = null;
        //运行垃圾回收器。
        System.gc();
        Thread.sleep(600);

        if(escapeGC !=null) {
            System.out.println("escapeGC Object not  null");
        } else {
            System.out.println("escapeGC Object is null");
        }
    }
}
2.5 方法区回收

类需要同时满足下面三个条件才能算无用的类
1、该类所有的实例已经被回收,也就是Java堆中不存在该类的任何实例
2、加载该类的ClassLoder已经被回收
3、该类对应的java.lang.Class对象没有在任何地方被引用,无 法通过任何地方通过反射访问该类的方法

3. 垃圾收集算法

1、标记-清除算法(标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象)
缺点:1、标识与清除是效率不高,2、会产生大量不连续内存碎片
这里写图片描述

2、复制算法:他将可用的内存按容量划分大小相等的两块,每次只使用其中一块。当这块的内存用完了,就将存活的对象复制到另外一块上面,然后再把已使用过得内存空间一次清理。(新生代比较适合)
这里写图片描述

3、标记-整理算法:左右存活的对象都往一遍移,然后直接清理掉边界以外的内存(老年代比较适合)
这里写图片描述

4. 垃圾收集器

垃圾收集器是内存回收的具体实现

5. 内存分配与回收策略

5.1 对象优先在Eden分配

对象在新生代Eden区中分配内存,当Eden去没有足够的空间进行分配时,虚拟机将发起一次Minor GC

package com.one.jvm;

/**
 * -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
 * 参数解释
 * -Xms20M:设置JVM促使内存为10M。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
 * -Xmx20M :设置JVM最大可用内存为20M
 * -Xmn10M :设置年轻代大小为10M。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
 * -XX:PrintGCDetails,开启详细GC日志模式
 * -XX:SurvivorRatio=8:年轻代中Eden区与两个Survivor区的比值(8:1)
 * @author Administrator
 *
 */
public class EdenAllocation {

    private static final int _1MB = 1024*1024;

    public static void main(String[] args) {

        byte[] eden4 = new byte[2*_1MB];
        byte[] eden5 = new byte[2*_1MB];
        byte[] eden6 = new byte[2*_1MB];
        //新生代垃圾回收
        byte[] eden7 = new byte[2*_1MB];
    }
}

日志

[GC [PSYoungGen: 6495K->304K(9216K)] 6495K->6448K(19456K), 0.0091599 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[Full GC [PSYoungGen: 304K->0K(9216K)] [PSOldGen: 6144K->6328K(10240K)] 6448K->6328K(19456K) [PSPermGen: 2967K->2967K(21248K)], 0.0069367 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
Heap
 PSYoungGen      total 9216K, used 2375K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 29% used [0x00000000ff600000,0x00000000ff851f98,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
  to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
 PSOldGen        total 10240K, used 6328K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 61% used [0x00000000fec00000,0x00000000ff22e140,0x00000000ff600000)
 PSPermGen       total 21248K, used 2984K [0x00000000f9a00000, 0x00000000faec0000, 0x00000000fec00000)
  object space 21248K, 14% used [0x00000000f9a00000,0x00000000f9cea3a0,0x00000000faec0000)

日志参数含义

  • 第一行GC是指发生在新生代的垃圾收集动作,PSYoungGen表示Parallel Scavenge垃圾回收器,
    6495K->304K(9216K):6495K表示新生代垃圾回收前的大小,304K表示新生代垃圾回收后的大小。9216K表示新生代的大小。6495K->6448K(19456K):6495K表示整个堆回收前的大小,6448K表示整个堆回收后的大小,19456K表示整个堆内存分配大小

  • 第二行Full GC指发生在老年代的垃圾收集动作,PSYoungGen表示Parallel
    Scavenge垃圾回收器,304K->0K(9216K):304K表示新生代垃圾回收前的大小,0K表示新生代垃圾回收后的大小。 PSOldGen表示垃圾回收器,6144K->6328K(10240K):6144K表示老年代垃圾回收前的大小,6328K表示老年代垃圾回收后的大小。10240K表示整个老年代的大小。10240K表示整个老年代的大小。6448K->6328K(19456K):6448K表示整个堆回收前的大小,6328K表示整个堆回收的大小,19456K表示整个堆内存

GC内存走向分析

  • 第一次新生代GC的内存走向分析
    6495K->304K,新生代回收的内存大小(6191K=6495K-304K)。发出这样的疑问?6191K到底去向何处。分析一下,
    6495K->6448K,整个堆回收的大小(47K)=6495K -
    6448K;剩下内存去那儿了(6191K-47K=6144K),哈哈,6144K去老年代了(有一段日志:[PSOldGen:
    6144K->6328K(10240K)])
  • 第二次老年代GC的内存走向分析
    304K->0K,在进行老年代GC时,首先把新生代的内存回收完毕。那么会产生一个疑问,清理的304K去向何处?分析一下,6144K->6328K,老年代增加了184K,整个内存堆回收了(6448K->6328K)120K。184K+120K=304K。
5.2 大对象直接进入老年代

所谓的大对象是指,需要大量连续内存空间的java对象,

package com.one.jvm;

/**
 * -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
 * 参数解释
 * -Xms20M:设置JVM促使内存为10M。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
 * -Xmx20M :设置JVM最大可用内存为20M
 * -Xmn10M :设置年轻代大小为10M。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
 * -XX:PrintGCDetails,开启详细GC日志模式
 * -XX:SurvivorRatio=8:年轻代中Eden区与两个Survivor区的比值(8:1)
 * @author Administrator
 *
 */
public class EdenAllocation {

    private static final int _1MB = 1024*1024;

    public static void main(String[] args) {

        byte[] eden1 = new byte[8*_1MB];

    }
}

GC日志:
Heap
PSYoungGen total 9216K, used 515K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 6% used [0x00000000ff600000,0x00000000ff680ee8,0x00000000ffe00000)
from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
to space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
PSOldGen total 10240K, used 8192K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
object space 10240K, 80% used [0x00000000fec00000,0x00000000ff400010,0x00000000ff600000)
PSPermGen total 21248K, used 2975K [0x00000000f9a00000, 0x00000000faec0000, 0x00000000fec00000)
object space 21248K, 14% used [0x00000000f9a00000,0x00000000f9ce7e48,0x00000000faec0000)

长期存活的对象进入老年代

动态对象年龄判定

空间分配担保

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值