<JVM上篇:内存与垃圾回收篇>05-本地方法接口和本地方法栈 | 06-堆(三)

简介: <JVM上篇:内存与垃圾回收篇>05-本地方法接口和本地方法栈 | 06-堆

6.4.2. 对象分配流程图

2ebf41b5828390cb44ab8c3169a88b47.png

6.4.3. 对象分配代码演示

/**
 * -Xms600m -Xmx600m
 * @author shkstart  shkstart@126.com
 * @create 2020  17:51
 */
public class HeapInstanceTest {
    byte[] buffer = new byte[new Random().nextInt(1024 * 200)];
    public static void main(String[] args) {
        ArrayList<HeapInstanceTest> list = new ArrayList<HeapInstanceTest>();
        while (true) {
            list.add(new HeapInstanceTest());
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果


6b14c0c487ee99577283aa09bd4789ef.png


6.4.4. 常用调优工具


在 JVM 下篇:性能监控与调优篇会详细介绍


JDK 命令行

Eclipse:Memory Analyzer Tool

Jconsole

VisualVM

Jprofiler

Java Flight Recorder

GCViewer

GC Easy

6.5. Minor GC、MajorGC和Full GC对比


6.5.1. GC的分类


JVM 在进行 GC 时,并非每次都对上面三个内存区域(新生区,老年区,方法区)一起回收的,大部分时候回收的都是指新生代。


针对 Hotspot VM 的实现,它里面的 GC 按照回收区域又分为两大种类型:一种是部分收集(Partial GC),一种是整堆收集(FullGC)


部分收集:不是完整收集整个 Java 堆的垃圾收集。其中又分为:

新生代收集(Minor GC / Young GC):只是新生代的垃圾收集

老年代收集(Major GC / Old GC):只是老年代的圾收集。

目前,只有 CMSGC 会有单独收集老年代的行为。

注意,很多时候 Major GC 会和 Full GC 混淆使用,需要具体分辨是老年代回收还是整堆回收。

混合收集(MixedGC):收集整个新生代以及部分老年代的垃圾收集。

目前,只有 G1 GC 会有这种行为

整堆收集(Full GC):收集整个 java 堆和方法区(程序执行过程中需要加载的类)的垃圾收集。


6.5.2. 年轻代 GC(Minor GC)触发机制


当年轻代空间不足时,就会触发 MinorGC。这里的年轻代满指的是 Eden 代满==,Survivor 满不会引发 GC==。(每次 Minor GC 会清理年轻代的内存。)

因为Java 对象大多都具备朝生夕灭的特性.,所以 Minor GC 非常频繁,一般回收速度也比较快。这一定义既清晰又易于理解。

Minor GC 会引发 STW,暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行

**备注:**当Survivor满了,超出部分直接放在老年区,其他部分保持不变.等下次Eden满了,触发Minor GC,再对Survivor进行垃圾清理. 即 Survivor区的垃圾回收是被动的


threshold:阈值


0f9825aaa0712c13ad4be1e7081e203b.png


6.5.3. 老年代 GC(Major GC / Full GC)触发机制


指发生在老年代的 GC,对象从老年代消失时,我们说 “Major GC” 或 “Full GC” 发生了


出现了 Major Gc,经常会伴随至少一次的 Minor GC(但非绝对的,在 Paralle1 Scavenge 收集器的收集策略里就有直接进行 MajorGC 的策略选择过程)


也就是在老年代空间不足时,会先尝试触发 Minor Gc。如果之后空间还不足,则触发 Major GC(??)

Major GC 的速度一般会比 Minor GC 慢 10 倍以上,STW 的时间更长(所以后序调优时,要尽量减少其出现次数.)


如果 Major GC 后,内存还不足,就报 OOM 了


6.5.4. Full GC 触发机制(后面细讲)


触发 Full GC 执行的情况有如下五种:


调用 System.gc()时,系统建议执行 Full GC,但是不必然执行

老年代空间不足

方法区空间不足

通过 Minor GC 后进入老年代的平均大小大于老年代的可用内存

由 Eden 区、survivor space0(From Space)区向 survivor space1(To Space)区复制时,对象大小大于 To Space 可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小,则会触发。

说明:Full GC 是开发或调优中尽量要避免的。这样暂时时间会短一些


6.5.5. GC举例与日志分析

/**
 * 测试MinorGC 、 MajorGC、FullGC
 * -Xms9m -Xmx9m -XX:+PrintGCDetails
 * @author shkstart  shkstart@126.com
 * @create 2020  14:19
 */
public class GCTest {
    public static void main(String[] args) {
        int i = 0;
        try {
            List<String> list = new ArrayList<>();
            String a = "atguigu.com";
            while (true) {
                list.add(a);
                a = a + a;
                i++;
            }
        } catch (Throwable t) {
            t.printStackTrace();
            System.out.println("遍历次数为:" + i);
        }
    }
}

运行结果


16db9a19d1d8903752571f4a0312705f.png


注意:

1.7之后字符串常量池就放到堆了,1.7之前是在方法区


6.6. 堆空间分代思想


为什么要把 Java 堆分代?不分代就不能正常工作了吗?


经研究,不同对象的生命周期不同。70%-99%的对象是临时对象。


新生代:有 Eden、两块大小相同的 survivor(又称为 from/to,s0/s1)构成,to 总为空。

老年代:存放新生代中经历多次 GC 仍然存活的对象。


ee1656b6f2d9681d04a5a31153e240fc.png


其实不分代完全可以,分代的唯一理由就是优化 GC 性能。如果没有分代,那所有的对象都在一块,就如同把一个学校的人都关在一个教室。GC 的时候要找到哪些对象没用,这样就会对堆的所有区域进行扫描。而很多对象都是朝生夕死的,如果分代的话,把新创建的对象放到某一地方,当 GC 的时候先把这块存储“朝生夕死”对象的区域进行回收,这样就会腾出很大的空间出来。


a1d9f7229e67c5be286f7fe3610e501f.png


6.7. 内存分配策略


如果对象在 Eden 出生并经过第一次 Minor GC 后仍然存活,并且能被 Survivor 容纳的话,将被移动到 survivor 空间中,并将对象年龄设为 1。对象在 survivor 区中每熬过一次 MinorGC,年龄就增加 1 岁,当它的年龄增加到一定程度(默认为 15 岁,其实每个 JVM、每个 GC 都有所不同)时,就会被晋升到老年代


对象晋升老年代的年龄阀值,可以通过选项-XX:MaxTenuringThreshold来设置


针对不同年龄段的对象分配原则如下所示:


优先分配到 Eden

大对象直接分配到老年代(尽量避免程序中出现过多的大对象)

长期存活的对象分配到老年代

动态对象年龄判断:如果 survivor 区中相同年龄的所有对象大小的总和大于 Survivor 空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。

空间分配担保: -XX:HandlePromotionFailure

注意:GC年龄在对象头中用4个字节存储,最大就是15,不可能超过15的


代码演示

/** 测试:大对象直接进入老年代
 * -Xms60m -Xmx60m -XX:NewRatio=2 -XX:SurvivorRatio=8 -XX:+PrintGCDetails
 * @author shkstart  shkstart@126.com
 * @create 2020  21:48
 */
public class YoungOldAreaTest {
    public static void main(String[] args) {
        byte[] buffer = new byte[1024 * 1024 * 20];//20m
    }
}

3c553a6e4d0e1c3434fff45d8595ed5f.png


相关文章
|
Arthas 存储 算法
深入理解JVM,包含字节码文件,内存结构,垃圾回收,类的声明周期,类加载器
JVM全称是Java Virtual Machine-Java虚拟机JVM作用:本质上是一个运行在计算机上的程序,职责是运行Java字节码文件,编译为机器码交由计算机运行类的生命周期概述:类的生命周期描述了一个类加载,使用,卸载的整个过类的生命周期阶段:类的声明周期主要分为五个阶段:加载->连接->初始化->使用->卸载,其中连接中分为三个小阶段验证->准备->解析类加载器的定义:JVM提供类加载器给Java程序去获取类和接口字节码数据类加载器的作用:类加载器接受字节码文件。
1071 55
|
9月前
|
弹性计算 定位技术 数据中心
阿里云服务器配置选择方法:付费类型、地域及CPU内存配置全解析
阿里云服务器怎么选?2025最新指南:就近选择地域,降低延迟;长期使用选包年包月,短期灵活选按量付费;企业选2核4G5M仅199元/年,个人选2核2G3M低至99元/年,高性价比爆款推荐,轻松上云。
1087 11
|
存储 安全 iOS开发
内存卡怎么格式化?6个格式化方法供你选
随着使用时间的增加,内存卡可能会因为数据积累、兼容性或是文件系统损坏等原因需要进行格式化。那么怎样正确格式化内存卡呢?格式化内存卡的时候需要注意什么呢?本文会给大家提供详细的步骤,帮助大家轻松完成格式化内存卡的操作。
|
缓存 Prometheus 监控
Elasticsearch集群JVM调优设置合适的堆内存大小
Elasticsearch集群JVM调优设置合适的堆内存大小
2821 1
|
监控 JavaScript Java
Node.js中内存泄漏的检测方法
检测内存泄漏需要综合运用多种方法,并结合实际的应用场景和代码特点进行分析。及时发现和解决内存泄漏问题,可以提高应用的稳定性和性能,避免潜在的风险和故障。同时,不断学习和掌握内存管理的知识,也是有效预防内存泄漏的重要途径。
1122 159
|
缓存 监控 算法
JVM简介—2.垃圾回收器和内存分配策略
本文介绍了Java垃圾回收机制的多个方面,包括垃圾回收概述、对象存活判断、引用类型介绍、垃圾收集算法、垃圾收集器设计、具体垃圾回收器详情、Stop The World现象、内存分配与回收策略、新生代配置演示、内存泄漏和溢出问题以及JDK提供的相关工具。
JVM简介—2.垃圾回收器和内存分配策略
|
存储 Windows
内存卡坏了还能修吗?4种常见修复方法
内存卡出现“无法保存”或“存储异常”等问题时,不一定是硬件损坏,可能是系统错误或文件系统异常导致。本文介绍几种亲测有效的修复方法:1) 更换读卡设备排除接触问题;2) 格式化修复文件系统(需先备份数据);3) 使用DiskGenius检测坏道;4) 借助厂商工具深度修复。同时提供日常保养建议,如避免高温环境、养成数据备份习惯,延长内存卡使用寿命。通过这些方法,多数问题可轻松解决,无需更换硬件。
|
缓存 监控 算法
Python内存管理:掌握对象的生命周期与垃圾回收机制####
本文深入探讨了Python中的内存管理机制,特别是对象的生命周期和垃圾回收过程。通过理解引用计数、标记-清除及分代收集等核心概念,帮助开发者优化程序性能,避免内存泄漏。 ####
550 35
|
传感器 人工智能 物联网
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发,以及面临的挑战和未来趋势,旨在帮助读者深入了解并掌握这些关键技术。
531 6