[jjzhu学java]之深入理解JVM之垃圾收集器与内存分配策略

简介: 深入理解JVM之垃圾收集器与内存分配策略如何判断对象已经消亡引用计数算法根搜索算法引用深入理解JVM之垃圾收集器与内存分配策略java中对象的创建需要的内存都是在java堆中申请的,所以垃圾收集的区域就是对java堆和方法区的内存区域进行GC。如何判断对象已经消亡垃圾收集器的主要任务就是找出已经“消亡”的对象,将其标记并

深入理解JVM之垃圾收集器与内存分配策略

java中对象的创建需要的内存都是在java堆中申请的,所以垃圾收集的区域就是对java堆和方法区的内存区域进行GC。

如何判断对象已经消亡

垃圾收集器的主要任务就是找出已经“消亡”的对象,将其标记并清除其说用内存的过程,如何判断某个对象已经“消亡”,不同的虚拟机有不同的判断策略

引用计数算法

引用计数(Reference Counting)算法的基本思想就是:给每个对象添加一个引用计数器,每当有一个地方对该对象进行了引用,引用计数器就加1;引用失效后就减1;当引用计数器为0时,就表示没有任何地方引用了该对象,这可以认为该对象已经“消亡”了。
虽然引用计数算法思想简单,效率也很高,但是java虚拟机并没有用到该算法标记“消亡”对象,因为当出现循环引用的时候,表现就不是那么好了。我们可以编写测试代码测试并看GC信息看看java虚拟机到底有没有用该算法。

/** 
* @ClassName: ReferenceCountingGC 
* @Description: 引用计算GC 
* @author 祝佳俊(jjzhu_ncu@163.com)
* @date 2016年10月20日 下午4:57:53 
*  
*/
public class ReferenceCountingGC {
    public Object instance = null;
    private static final int _1MB = 1024 * 1024;
    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;

        System.gc();
    }
    public static void main(String[] args) {
        testGC();
    }
}

代码示例中,新建了两个对象objA,objB,然后通过objA.instance = objB,objB.instance = objA让他们互相引用,然后将objA、objB都置为空,将会触发GC,我们可以通过-XX:+PrintGCDetails让java虚拟机在发生GC后打印出GC的具体信息,
代码片运行时的VM参数为-Xmx20M -Xms20M -XX:+PrintGCDetails
运行程序,可看到如下打印结果:

[GC [PSYoungGen: 4340K->256K(5952K)] 4340K->256K(19648K), 0.0009373 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System) [PSYoungGen: 256K->0K(5952K)] [PSOldGen: 0K->162K(13696K)] 256K->162K(19648K) [PSPermGen: 3025K->3025K(21248K)], 0.0053905 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
Heap
 PSYoungGen      total 5952K, used 205K [0x00000000ff960000, 0x0000000100000000, 0x0000000100000000)
  eden space 5120K, 4% used [0x00000000ff960000,0x00000000ff993408,0x00000000ffe60000)
  from space 832K, 0% used [0x00000000ffe60000,0x00000000ffe60000,0x00000000fff30000)
  to   space 832K, 0% used [0x00000000fff30000,0x00000000fff30000,0x0000000100000000)
 PSOldGen        total 13696K, used 162K [0x00000000fec00000, 0x00000000ff960000, 0x00000000ff960000)
  object space 13696K, 1% used [0x00000000fec00000,0x00000000fec28910,0x00000000ff960000)
 PSPermGen       total 21248K, used 3043K [0x00000000f9a00000, 0x00000000faec0000, 0x00000000fec00000)
  object space 21248K, 14% used [0x00000000f9a00000,0x00000000f9cf8c28,0x00000000faec0000)

在分析结果前,先对GC的内容先做一个介绍:

1、GC日志的第一行[GC [PSYoungGen说明了在新生代发生了GC(Minor GC),这里也可以看出,当前的HotSpot虚拟机采用的是Parallel Scavenge(PS)垃圾收集器 后面的4340K->256K代表GC前后的新生代内存区域的变化,这里从4340K变到256K,说明虚拟机进行了垃圾回收,后面的(5952K)代表新生代的内存区域大小,4340K->256K(19648K)这里的括号内的19648K代表新生代和年老代的内存大小。之后的0.0009373 secs代表的是GC所用的事件。
2、GC日志的第二行[Full GC (System)表示系统触发的一次Full GC,也就是代码中System.gc();所引起的GC,一次Full GC会对java堆中的所有区域进行GC(新生代、年老代、永久代),所以后面的PSYoungGen(新生代)、PSOldGen(年老代)、PSPermGen(永久带)显示了各区域的GC情况,我们可以看到PSYoungGen: 256K->0K(5952K),新生代经过Full GC后,全被清空了。
3、后面的Heap堆显示了各区域的最终使用情况
从最后的Full GC (System) [PSYoungGen: 256K->0K(5952K)]可以看到,新生代中的内存全被GC了,所以说,HotSpot并没有用引用计数算法来对“消亡”对象进行GC。

根搜索算法

现在一般的垃圾收集器都是用该算法(GC Root Tracing)来判断对象是否“消亡”,该算法的基本思想就是:通过一组称为“GC Roots”的对象作为根节点,然后从这些根节点向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到根节点之间没有任何一条引用链的话,就认为该对象已经“消亡”,如下图所示:

可以作为GC Roots的对象包括:
1. 虚拟机栈(栈帧中的本地变量表)中的引用的对象
2. 方法区中 的类静态属性引用的对象
3. 方法区中的常量引用的对象
4. Native方法引用的对象

引用

任何判断对象是否消亡都是通过引用来判断,引用以前的定义是:如果reference类型的数据中存储的数值代表的是另一块内存区域的起始地址的话,就称这块内存代表一个引用

目录
相关文章
|
11月前
|
Arthas 存储 算法
深入理解JVM,包含字节码文件,内存结构,垃圾回收,类的声明周期,类加载器
JVM全称是Java Virtual Machine-Java虚拟机JVM作用:本质上是一个运行在计算机上的程序,职责是运行Java字节码文件,编译为机器码交由计算机运行类的生命周期概述:类的生命周期描述了一个类加载,使用,卸载的整个过类的生命周期阶段:类的声明周期主要分为五个阶段:加载->连接->初始化->使用->卸载,其中连接中分为三个小阶段验证->准备->解析类加载器的定义:JVM提供类加载器给Java程序去获取类和接口字节码数据类加载器的作用:类加载器接受字节码文件。
930 55
|
12月前
|
Arthas 监控 Java
Arthas memory(查看 JVM 内存信息)
Arthas memory(查看 JVM 内存信息)
911 6
|
6月前
|
存储 缓存 Java
我们来说一说 JVM 的内存模型
我是小假 期待与你的下一次相遇 ~
474 5
|
6月前
|
存储 缓存 算法
深入理解JVM《JVM内存区域详解 - 世界的基石》
Java代码从编译到执行需经javac编译为.class字节码,再由JVM加载运行。JVM内存分为线程私有(程序计数器、虚拟机栈、本地方法栈)和线程共享(堆、方法区)区域,其中堆是GC主战场,方法区在JDK 8+演变为使用本地内存的元空间,直接内存则用于提升NIO性能,但可能引发OOM。
|
8月前
|
安全 Oracle Java
JAVA高级开发必备·卓伊凡详细JDK、JRE、JVM与Java生态深度解析-形象比喻系统理解-优雅草卓伊凡
JAVA高级开发必备·卓伊凡详细JDK、JRE、JVM与Java生态深度解析-形象比喻系统理解-优雅草卓伊凡
591 0
JAVA高级开发必备·卓伊凡详细JDK、JRE、JVM与Java生态深度解析-形象比喻系统理解-优雅草卓伊凡
|
存储 缓存 算法
JVM简介—1.Java内存区域
本文详细介绍了Java虚拟机运行时数据区的各个方面,包括其定义、类型(如程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区和直接内存)及其作用。文中还探讨了各版本内存区域的变化、直接内存的使用、从线程角度分析Java内存区域、堆与栈的区别、对象创建步骤、对象内存布局及访问定位,并通过实例说明了常见内存溢出问题的原因和表现形式。这些内容帮助开发者深入理解Java内存管理机制,优化应用程序性能并解决潜在的内存问题。
687 29
JVM简介—1.Java内存区域
|
缓存 监控 算法
JVM简介—2.垃圾回收器和内存分配策略
本文介绍了Java垃圾回收机制的多个方面,包括垃圾回收概述、对象存活判断、引用类型介绍、垃圾收集算法、垃圾收集器设计、具体垃圾回收器详情、Stop The World现象、内存分配与回收策略、新生代配置演示、内存泄漏和溢出问题以及JDK提供的相关工具。
JVM简介—2.垃圾回收器和内存分配策略
|
9月前
|
存储
阿里云轻量应用服务器收费标准价格表:200Mbps带宽、CPU内存及存储配置详解
阿里云香港轻量应用服务器,200Mbps带宽,免备案,支持多IP及国际线路,月租25元起,年付享8.5折优惠,适用于网站、应用等多种场景。
2897 0
|
9月前
|
存储 缓存 NoSQL
内存管理基础:数据结构的存储方式
数据结构在内存中的存储方式主要包括连续存储、链式存储、索引存储和散列存储。连续存储如数组,数据元素按顺序连续存放,访问速度快但扩展性差;链式存储如链表,通过指针连接分散的节点,便于插入删除但访问效率低;索引存储通过索引表提高查找效率,常用于数据库系统;散列存储如哈希表,通过哈希函数实现快速存取,但需处理冲突。不同场景下应根据访问模式、数据规模和操作频率选择合适的存储结构,甚至结合多种方式以达到最优性能。掌握这些存储机制是构建高效程序和理解高级数据结构的基础。
961 1