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

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

6.1.2. 堆空间内部结构(JDK7)


f21f44d5a488324d24596cc1d51a4291.jpg


6.1.3. 堆空间内部结构(JDK8)


a5efd5867fd49d1ac5deecdf6c428028.jpg


6.2. 设置堆内存大小与 OOM


6.2.1. 堆空间大小的设置


Java 堆区用于存储 Java 对象实例,那么堆的大小在 JVM 启动时就已经设定好了,大家可以通过选项"-Xmx"和"-Xms"来进行设置。


“-Xms"用于表示堆区的起始内存,等价于-XX:InitialHeapSize

“-Xmx"则用于表示堆区的最大内存,等价于-XX:MaxHeapSize

一旦堆区中的内存大小超过“-Xmx"所指定的最大内存时,将会抛出 OutOfMemoryError 异常。


通常会将-Xms 和-Xmx 两个参数配置相同的值,其目的是为了能够在 Java 垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小,从而提高性能。


默认情况下


初始内存大小:物理电脑内存大小 / 64

最大内存大小:物理电脑内存大小 / 4

代码演示:

/**
 * 1. 设置堆空间大小的参数
 * -Xms 用来设置堆空间(年轻代+老年代)的初始内存大小
 *      -X 是jvm的运行参数
 *      ms 是memory start
 * -Xmx 用来设置堆空间(年轻代+老年代)的最大内存大小
 *
 * 2. 默认堆空间的大小
 *    初始内存大小:物理电脑内存大小 / 64
 *             最大内存大小:物理电脑内存大小 / 4
 * 3. 手动设置:-Xms600m -Xmx600m
 *     开发中建议将初始堆内存和最大的堆内存设置成相同的值。(避免堆区的扩容和释放,降低性能消耗)
 *
 * 4. 查看设置的参数:方式一: jps   /  jstat -gc 进程id
 *                  方式二:-XX:+PrintGCDetails  //程序执行完之后打印
 * @author shkstart  shkstart@126.com
 * @create 2020  20:15
 */
public class HeapSpaceInitial {
    public static void main(String[] args) {
        //返回Java虚拟机中的堆内存总量
        long initialMemory = Runtime.getRuntime().totalMemory() / 1024 / 1024;
        //返回Java虚拟机试图使用的最大堆内存量
        long maxMemory = Runtime.getRuntime().maxMemory() / 1024 / 1024;
        System.out.println("-Xms : " + initialMemory + "M");
        System.out.println("-Xmx : " + maxMemory + "M");
       System.out.println("系统内存大小为:" + initialMemory * 64.0 / 1024 + "G");
       System.out.println("系统内存大小为:" + maxMemory * 4.0 / 1024 + "G");
        // try {
        //     Thread.sleep(1000000);
        // } catch (InterruptedException e) {
        //     e.printStackTrace();
        // }
    }
}

结果分析


不进行任何配置,运行之

f66b51da89a137fb9f4954928506b8a4.jpg


手动设置:-Xms600m -Xmx600m,运行之. 我们出现了疑惑,后面进行解密!哈哈哈.

325842cfe166280d615c1dd4090ae7a9.png


打开try…catch…,进行查看分析.

1a73067dd428aa8d2ee9f6a0fb0f4be2.png


使用PrintGCDetails,查看堆内存的详细信息.

945c86797ef62c1a77b5c9fd6a55354d.png


6.2.2. OutOfMemory 举例

/**
 * -Xms600m -Xmx600m
 * @author shkstart  shkstart@126.com
 * @create 2020  21:12
 */
public class OOMTest {
    public static void main(String[] args) {
        ArrayList<Picture> list = new ArrayList<>();
        while(true){
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            list.add(new Picture(new Random().nextInt(1024 * 1024)));
        }
    }
}
class Picture{
    private byte[] pixels;
    public Picture(int length) {
        this.pixels = new byte[length];
    }
}
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
  at com.atguigu.java.Picture.<init>(OOMTest.java:29)
  at com.atguigu.java.OOMTest.main(OOMTest.java:20)

运行观察结果:


70a08a9124b4110cd54f42435347014d.png


image.png


6.3. 年轻代与老年代


存储在 JVM 中的 Java 对象可以被划分为两类:


一类是生命周期较短的瞬时对象,这类对象的创建和消亡都非常迅速

另外一类对象的生命周期却非常长,在某些极端的情况下还能够与 JVM 的生命周期保持一致

Java 堆区进一步细分的话,可以划分为年轻代(YoungGen)和老年代(oldGen)


其中年轻代又可以划分为 Eden 空间、Survivor0 空间和 Survivor1 空间(有时也叫做 from 区、to 区)


394d7e5831f9c7af84189af97901842b.jpg


下面这参数开发中一般不会调:


7d9c2d1f9ef9f315f01b3441c6e81660.png


配置新生代与老年代在堆结构的占比。


默认-XX:NewRatio=2,表示新生代占 1,老年代占 2,新生代占整个堆的 1/3

可以修改-XX:NewRatio=4,表示新生代占 1,老年代占 4,新生代占整个堆的 1/5

在 HotSpot 中,Eden 空间和另外两个 survivor 空间缺省所占的比例是 8:1:1 (因为有个自适应的内存分配原则,实际测试6:1:1)


当然开发人员可以通过选项“-xx:SurvivorRatio”调整这个空间比例。比如-xx:SurvivorRatio=8


几乎所有的 Java 对象都是在 Eden 区被 new 出来的。绝大部分的 Java 对象的销毁都在新生代进行了。


IBM 公司的专门研究表明,新生代中 80%的对象都是“朝生夕死”的。

可以使用选项"-Xmn"设置新生代最大内存大小,这个参数一般使用默认值就可以了。


83c89e98a7ea9b9ac1dd92af5ad8d1eb.png


图解:对象在Eden一段时间内没有被销毁,进入Survivor区,长时间仍未销毁进入老年区.


代码演示:

/**
 * -Xms600m -Xmx600m
 *
 * -XX:NewRatio : 设置新生代与老年代的比例。默认值是2.
 * -XX:SurvivorRatio :设置新生代中Eden区与Survivor区的比例。默认值是8
 * -XX:-UseAdaptiveSizePolicy :关闭自适应的内存分配策略  (暂时用不到)
 * -Xmn:设置新生代的空间的大小。 (一般不设置)
 * 备注:-Xmn优先级比-XX:NewRatio高,所以当两个同时设置时(不推荐),会以前者为主
 * @author shkstart  shkstart@126.com
 * @create 2020  17:23
 */
public class EdenSurvivorTest {
    public static void main(String[] args) {
        System.out.println("我只是来打个酱油~");
        try {
            Thread.sleep(1000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

结果分析


2d3e59df16edb439fb41a4d9342a0128.png


6.4. 图解对象分配过程


6.4.1. 对象分配基本过程分析


为新对象分配内存是一件非常严谨和复杂的任务,JVM 的设计者们不仅需要考虑内存如何分配、在哪里分配等问题,并且由于内存分配算法与内存回收算法密切相关,所以还需要考虑 GC 执行完内存回收后是否会在内存空间中产生内存碎片。


new 的对象先放伊甸园区。此区有大小限制。


当伊甸园的空间填满时,程序又需要创建对象,JVM 的垃圾回收器将对伊甸园区进行垃圾回收(MinorGC),将伊甸园区中的不再被其他对象所引用的对象进行销毁。再加载新的对象放到伊甸园区


然后将伊甸园中的剩余对象移动到幸存者 0 区。


如果再次触发垃圾回收,此时上次幸存下来的放到幸存者 0 区的,如果没有回收,就会放到幸存者 1 区。


如果再次经历垃圾回收,此时会重新放回幸存者 0 区,接着再去幸存者 1 区。


啥时候能去养老区呢?可以设置次数。默认是 15 次。


可以设置参数:-Xx:MaxTenuringThreshold= N进行设置

在养老区,相对悠闲。当养老区内存不足时,再次触发 GC:Major GC,进行养老区的内存清理


若养老区执行了 Major GC 之后,发现依然无法进行对象的保存,就会产生 OOM 异常。

java.lang.OutofMemoryError: Java heap space

b0579c1f1eac678ab24a800829a81138.jpg

总结

  • 针对幸存者 s0,s1 区的总结:复制之后有交换,谁空谁是 to
  • 关于垃圾回收:频繁在新生区收集,很少在老年代收集,几乎不再永久代和元空间进行收集
相关文章
|
4月前
|
Arthas 存储 算法
深入理解JVM,包含字节码文件,内存结构,垃圾回收,类的声明周期,类加载器
JVM全称是Java Virtual Machine-Java虚拟机JVM作用:本质上是一个运行在计算机上的程序,职责是运行Java字节码文件,编译为机器码交由计算机运行类的生命周期概述:类的生命周期描述了一个类加载,使用,卸载的整个过类的生命周期阶段:类的声明周期主要分为五个阶段:加载->连接->初始化->使用->卸载,其中连接中分为三个小阶段验证->准备->解析类加载器的定义:JVM提供类加载器给Java程序去获取类和接口字节码数据类加载器的作用:类加载器接受字节码文件。
405 55
|
9月前
|
监控 算法 Java
Java虚拟机(JVM)垃圾回收机制深度剖析与优化策略####
本文作为一篇技术性文章,深入探讨了Java虚拟机(JVM)中垃圾回收的工作原理,详细分析了标记-清除、复制算法、标记-压缩及分代收集等主流垃圾回收算法的特点和适用场景。通过实际案例,展示了不同GC(Garbage Collector)算法在应用中的表现差异,并针对大型应用提出了一系列优化策略,包括选择合适的GC算法、调整堆内存大小、并行与并发GC调优等,旨在帮助开发者更好地理解和优化Java应用的性能。 ####
221 27
|
4月前
|
缓存 算法 Java
JVM深入原理(八)(一):垃圾回收
弱引用-作用:JVM中使用WeakReference对象来实现软引用,一般在ThreadLocal中,当进行垃圾回收时,被弱引用对象引用的对象就直接被回收.软引用-作用:JVM中使用SoftReference对象来实现软引用,一般在缓存中使用,当程序内存不足时,被引用的对象就会被回收.强引用-作用:可达性算法描述的根对象引用普通对象的引用,指的就是强引用,只要有这层关系存在,被引用的对象就会不被垃圾回收。引用计数法-缺点:如果两个对象循环引用,而又没有其他的对象来引用它们,这样就造成垃圾堆积。
130 0
|
4月前
|
算法 Java 对象存储
JVM深入原理(八)(二):垃圾回收
Java垃圾回收过程会通过单独的GC线程来完成,但是不管使用哪一种GC算法,都会有部分阶段需要停止所有的用户线程。这个过程被称之为StopTheWorld简称STW,如果STW时间过长则会影响用户的使用。一般来说,堆内存越大,最大STW就越长,想减少最大STW,就会减少吞吐量,不同的GC算法适用于不同的场景。分代回收算法将整个堆中的区域划分为新生代和老年代。--超过新生代大小的大对象会直接晋升到老年代。
95 0
|
6月前
|
缓存 监控 算法
JVM简介—2.垃圾回收器和内存分配策略
本文介绍了Java垃圾回收机制的多个方面,包括垃圾回收概述、对象存活判断、引用类型介绍、垃圾收集算法、垃圾收集器设计、具体垃圾回收器详情、Stop The World现象、内存分配与回收策略、新生代配置演示、内存泄漏和溢出问题以及JDK提供的相关工具。
JVM简介—2.垃圾回收器和内存分配策略
|
7月前
|
存储 IDE Java
java设置栈内存大小
在Java应用中合理设置栈内存大小是确保程序稳定性和性能的重要措施。通过JVM参数 `-Xss`,可以灵活调整栈内存大小,以适应不同的应用场景。本文介绍了设置栈内存大小的方法、应用场景和注意事项,希望能帮助开发者更好地管理Java应用的内存资源。
341 4
|
10月前
|
机器学习/深度学习 监控 算法
Java虚拟机(JVM)的垃圾回收机制深度剖析####
本文深入探讨Java虚拟机(JVM)的垃圾回收机制,揭示其工作原理、常见算法、性能调优策略及未来趋势。通过实例解析,为开发者提供优化Java应用性能的思路与方法。 ####
218 28
|
9月前
|
算法 网络协议 Java
【JVM】——GC垃圾回收机制(图解通俗易懂)
GC垃圾回收,标识出垃圾(计数机制、可达性分析)内存释放机制(标记清除、复制算法、标记整理、分代回收)
|
9月前
|
存储 算法 Java
Java 内存管理与优化:掌控堆与栈,雕琢高效代码
Java内存管理与优化是提升程序性能的关键。掌握堆与栈的运作机制,学习如何有效管理内存资源,雕琢出更加高效的代码,是每个Java开发者必备的技能。
221 5
|
9月前
|
存储 监控 算法
Java虚拟机(JVM)垃圾回收机制深度解析与优化策略####
本文旨在深入探讨Java虚拟机(JVM)的垃圾回收机制,揭示其工作原理、常见算法及参数调优方法。通过剖析垃圾回收的生命周期、内存区域划分以及GC日志分析,为开发者提供一套实用的JVM垃圾回收优化指南,助力提升Java应用的性能与稳定性。 ####

热门文章

最新文章