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

目录
相关文章
|
17天前
|
Oracle Java 关系型数据库
java体系结构和jvm
java体系结构和jvm
|
3天前
|
监控 Ubuntu Java
Java VisualVM远程监控JVM
Java VisualVM远程监控JVM
Java VisualVM远程监控JVM
|
8天前
|
缓存 监控 Java
深入理解Java虚拟机(JVM)性能调优
【4月更文挑战第18天】本文探讨了Java虚拟机(JVM)的性能调优,包括使用`jstat`、`jmap`等工具监控CPU、内存和GC活动,选择适合的垃圾回收器(如Serial、Parallel、CMS、G1),调整堆大小和新生代/老年代比例,以及代码优化和JIT编译策略。通过这些方法,开发者能有效提升应用性能并应对复杂性挑战。性能调优是持续过程,需伴随应用演进和环境变化进行监控与优化。
|
8天前
|
移动开发 Android开发 开发者
构建高效Android应用:采用Kotlin进行内存优化的策略
【4月更文挑战第18天】 在移动开发领域,性能优化一直是开发者关注的焦点。特别是对于Android应用而言,由于设备和版本的多样性,确保应用流畅运行且占用资源少是一大挑战。本文将探讨使用Kotlin语言开发Android应用时,如何通过内存优化来提升应用性能。我们将从减少不必要的对象创建、合理使用数据结构、避免内存泄漏等方面入手,提供实用的代码示例和最佳实践,帮助开发者构建更加高效的Android应用。
14 0
|
10天前
|
缓存 移动开发 Java
构建高效的Android应用:内存优化策略
【4月更文挑战第16天】 在移动开发领域,尤其是针对资源有限的Android设备,内存优化是提升应用性能和用户体验的关键因素。本文将深入探讨Android应用的内存管理机制,分析常见的内存泄漏问题,并提出一系列实用的内存优化技巧。通过这些策略的实施,开发者可以显著减少应用的内存占用,避免不必要的后台服务,以及提高垃圾回收效率,从而延长设备的电池寿命并确保应用的流畅运行。
|
14天前
|
存储 前端开发 安全
JVM内部世界(内存划分,类加载,垃圾回收)(上)
JVM内部世界(内存划分,类加载,垃圾回收)
48 0
|
14天前
|
算法 安全 Java
内存分配与回收策略
内存分配与回收策略
19 0
内存分配与回收策略
|
17天前
|
NoSQL 安全 Redis
redis内存限制与淘汰策略
Redis内存管理包括限制和淘汰策略。`maxmemory`配置参数决定内存上限,无设置时64位系统默认不限制,可能导致系统资源耗尽,生产环境建议设定合理值。当内存满时,未设置淘汰策略会导致写入错误。Redis提供8种淘汰策略,如LRU(最近最少使用)和LFU(最不经常使用),以及随机或基于过期时间的删除。需根据数据重要性、访问频率和一致性选择合适策略。
175 0
|
18天前
|
存储 算法 安全
深度解析JVM世界:JVM内存分配
深度解析JVM世界:JVM内存分配
|
18天前
|
算法 Java UED
【五一创作】值得一看的JVM垃圾收集器
【五一创作】值得一看的JVM垃圾收集器