<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


相关文章
|
10天前
|
监控 算法 Java
Java虚拟机(JVM)垃圾回收机制深度剖析与优化策略####
本文作为一篇技术性文章,深入探讨了Java虚拟机(JVM)中垃圾回收的工作原理,详细分析了标记-清除、复制算法、标记-压缩及分代收集等主流垃圾回收算法的特点和适用场景。通过实际案例,展示了不同GC(Garbage Collector)算法在应用中的表现差异,并针对大型应用提出了一系列优化策略,包括选择合适的GC算法、调整堆内存大小、并行与并发GC调优等,旨在帮助开发者更好地理解和优化Java应用的性能。 ####
15 0
|
7天前
|
存储 监控 算法
深入探索Java虚拟机(JVM)的内存管理机制
本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。
|
11天前
|
存储 算法 Java
Java 内存管理与优化:掌控堆与栈,雕琢高效代码
Java内存管理与优化是提升程序性能的关键。掌握堆与栈的运作机制,学习如何有效管理内存资源,雕琢出更加高效的代码,是每个Java开发者必备的技能。
40 5
|
9天前
|
存储 监控 算法
Java虚拟机(JVM)垃圾回收机制深度解析与优化策略####
本文旨在深入探讨Java虚拟机(JVM)的垃圾回收机制,揭示其工作原理、常见算法及参数调优方法。通过剖析垃圾回收的生命周期、内存区域划分以及GC日志分析,为开发者提供一套实用的JVM垃圾回收优化指南,助力提升Java应用的性能与稳定性。 ####
|
13天前
|
机器学习/深度学习 监控 算法
Java虚拟机(JVM)的垃圾回收机制深度剖析####
本文深入探讨Java虚拟机(JVM)的垃圾回收机制,揭示其工作原理、常见算法、性能调优策略及未来趋势。通过实例解析,为开发者提供优化Java应用性能的思路与方法。 ####
28 1
|
16天前
|
监控 算法 Java
Java虚拟机垃圾回收机制深度剖析与优化策略####
【10月更文挑战第21天】 本文旨在深入探讨Java虚拟机(JVM)中的垃圾回收机制,揭示其工作原理、常见算法及参数调优技巧。通过案例分析,展示如何根据应用特性调整GC策略,以提升Java应用的性能和稳定性,为开发者提供实战中的优化指南。 ####
33 5
|
16天前
|
Java
JVM内存参数
-Xmx[]:堆空间最大内存 -Xms[]:堆空间最小内存,一般设置成跟堆空间最大内存一样的 -Xmn[]:新生代的最大内存 -xx[use 垃圾回收器名称]:指定垃圾回收器 -xss:设置单个线程栈大小 一般设堆空间为最大可用物理地址的百分之80
|
17天前
|
Java
JVM运行时数据区(内存结构)
1)虚拟机栈:每次调用方法都会在虚拟机栈中产生一个栈帧,每个栈帧中都有方法的参数、局部变量、方法出口等信息,方法执行完毕后释放栈帧 (2)本地方法栈:为native修饰的本地方法提供的空间,在HotSpot中与虚拟机合二为一 (3)程序计数器:保存指令执行的地址,方便线程切回后能继续执行代码
18 3
|
16天前
|
存储 算法 Java
JVM进阶调优系列(10)敢向stop the world喊卡的G1垃圾回收器 | 有必要讲透
本文详细介绍了G1垃圾回收器的背景、核心原理及其回收过程。G1,即Garbage First,旨在通过将堆内存划分为多个Region来实现低延时的垃圾回收,每个Region可以根据其垃圾回收的价值被优先回收。文章还探讨了G1的Young GC、Mixed GC以及Full GC的具体流程,并列出了G1回收器的核心参数配置,帮助读者更好地理解和优化G1的使用。
|
16天前
|
算法 Java
JVM有哪些垃圾回收算法?
(1)标记清除算法: 标记不需要回收的对象,然后清除没有标记的对象,会造成许多内存碎片。 (2)复制算法: 将内存分为两块,只使用一块,进行垃圾回收时,先将存活的对象复制到另一块区域,然后清空之前的区域。用在新生代 (3)标记整理算法: 与标记清除算法类似,但是在标记之后,将存活对象向一端移动,然后清除边界外的垃圾对象。用在老年代
22 0