[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类型的数据中存储的数值代表的是另一块内存区域的起始地址的话,就称这块内存代表一个引用

目录
相关文章
|
3月前
|
缓存 Prometheus 监控
Elasticsearch集群JVM调优设置合适的堆内存大小
Elasticsearch集群JVM调优设置合适的堆内存大小
637 1
|
2月前
|
存储 Java 程序员
【JVM】——JVM运行机制、类加载机制、内存划分
JVM运行机制,堆栈,程序计数器,元数据区,JVM加载机制,双亲委派模型
|
2月前
|
存储 监控 算法
Java内存管理深度剖析:从垃圾收集到内存泄漏的全面指南####
本文深入探讨了Java虚拟机(JVM)中的内存管理机制,特别是垃圾收集(GC)的工作原理及其调优策略。不同于传统的摘要概述,本文将通过实际案例分析,揭示内存泄漏的根源与预防措施,为开发者提供实战中的优化建议,旨在帮助读者构建高效、稳定的Java应用。 ####
54 8
|
2月前
|
存储 监控 算法
深入探索Java虚拟机(JVM)的内存管理机制
本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。
|
3月前
|
Java
JVM内存参数
-Xmx[]:堆空间最大内存 -Xms[]:堆空间最小内存,一般设置成跟堆空间最大内存一样的 -Xmn[]:新生代的最大内存 -xx[use 垃圾回收器名称]:指定垃圾回收器 -xss:设置单个线程栈大小 一般设堆空间为最大可用物理地址的百分之80
|
3月前
|
Java
JVM运行时数据区(内存结构)
1)虚拟机栈:每次调用方法都会在虚拟机栈中产生一个栈帧,每个栈帧中都有方法的参数、局部变量、方法出口等信息,方法执行完毕后释放栈帧 (2)本地方法栈:为native修饰的本地方法提供的空间,在HotSpot中与虚拟机合二为一 (3)程序计数器:保存指令执行的地址,方便线程切回后能继续执行代码
38 3
|
3月前
|
存储 缓存 监控
Elasticsearch集群JVM调优堆外内存
Elasticsearch集群JVM调优堆外内存
73 1
|
3天前
|
Java 程序员 开发者
Java社招面试题:一个线程运行时发生异常会怎样?
大家好,我是小米。今天分享一个经典的 Java 面试题:线程运行时发生异常,程序会怎样处理?此问题考察 Java 线程和异常处理机制的理解。线程发生异常,默认会导致线程终止,但可以通过 try-catch 捕获并处理,避免影响其他线程。未捕获的异常可通过 Thread.UncaughtExceptionHandler 处理。线程池中的异常会被自动处理,不影响任务执行。希望这篇文章能帮助你深入理解 Java 线程异常处理机制,为面试做好准备。如果你觉得有帮助,欢迎收藏、转发!
37 14
|
6天前
|
安全 Java 程序员
Java 面试必问!线程构造方法和静态块的执行线程到底是谁?
大家好,我是小米。今天聊聊Java多线程面试题:线程类的构造方法和静态块是由哪个线程调用的?构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节有助于掌握Java多线程机制。下期再见! 简介: 本文通过一个常见的Java多线程面试题,详细讲解了线程类的构造方法和静态块是由哪个线程调用的。构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节对掌握Java多线程编程至关重要。
35 13
|
7天前
|
安全 Java 开发者
【JAVA】封装多线程原理
Java 中的多线程封装旨在简化使用、提高安全性和增强可维护性。通过抽象和隐藏底层细节,提供简洁接口。常见封装方式包括基于 Runnable 和 Callable 接口的任务封装,以及线程池的封装。Runnable 适用于无返回值任务,Callable 支持有返回值任务。线程池(如 ExecutorService)则用于管理和复用线程,减少性能开销。示例代码展示了如何实现这些封装,使多线程编程更加高效和安全。

热门文章

最新文章