JVM 内存模型(下)

简介: 本文主要讲述 JVM 内存模型, 解析各个运行时内存数据区域的作用和使用场景。 本文所提到的 JVM 模型都是基于 jdk-1.8 版本

JVM 内存参数设置


内存参数配置


image.png


Spring-Boot 程序的 JVM 内存参数设置格式(Tomcat 启动直接在 bin 目录下的 Catalina.sh 文件设置)


java -Xms2048m -Xmx2048m  -Xmn1024 -Xss512k -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m -jar  xxx-xxx.jar


关于元空间JVM 有两个:-XX:MetaspaceSize=N 和 -XX:MaxMetaspaceSize=N,对于 64 位 JVM 来说, 元空间默认是 21MB,默认的元空间的最大值是无限


-XX:MaxMetaspaceSize: 设置元空间最大值,默认是 -1, 即不限制,或者说是受限制于本地内存大小。


-XX:MetaspaceSize:指定元空间的初始大小,以字节为单位,默认是 21M,达到该值过后就会触发 full gc 进行类型卸载,同时收集器会对该值进行调整;如果释放了大量的空间就适当降低该值;如果释放了很少的空间,那么就在不超过 -XX:MaxMetaspaceSize (如果设置)的情况下,适当提高该值。


由于调整元空间大小需要 full gc , 这是一个非常昂贵的操作,如果在启动过程中发生大量 full gc, 通常都是由于永久代或者元空间发生了大小调整,基于这种情况,一般建议在 JVM 参数将 MaxMetaspaceSize 和 MetaspaceSize 设置成一样的值,并设置得比初始值要大,对于 8G 的物理内存来说我们通常都会将这两个值设置为 256M。


堆空间内存溢出


import java.util.ArrayList;
import java.util.List;
public class HeapOverFlowTest {
    byte[] a = new byte[1024 * 1024 * 2]; // 2mb
    public static void main(String[] args) {
        List<HeapOverFlowTest> list = new ArrayList<>();
        while(true) {
            list.add(new HeapOverFlowTest());
        }
    }
}
// 输出结果
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
  at cn.edu.cqvie.jvm.HeapOverFlowTest.<init>(HeapOverFlowTest.java:8)
  at cn.edu.cqvie.jvm.HeapOverFlowTest.main(HeapOverFlowTest.java:13)


虚拟机栈内存溢出


public class StackOverFlowTest {
    // JVM 设置
    // -Xss128k, -Xss默认1M
    static int count = 0;
    static void redo() {
        count++;
        redo();
    }
    public static void main(String[] args) {
        try {
            redo();
            System.out.println(count);
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }
}
// 输出结果: 栈溢出
java.lang.StackOverflowError
  at cn.edu.cqvie.jvm.StackOverFlowTest.redo(StackOverFlowTest.java:11)
  at cn.edu.cqvie.jvm.StackOverFlowTest.redo(StackOverFlowTest.java:11)
  at cn.edu.cqvie.jvm.StackOverFlowTest.redo(StackOverFlowTest.java:11)
....


总结:


-Xss 设置越小 count 值越小,说明一个线程栈里能够分配的栈帧就越小,但是对于 JVM 整体来说能够开启的线程数就会更多。


方法区内存溢出


  1. 需要注意的是 1.8 内模型中,将运行时常量池数据放入堆中,所以我们限制方法区的大小对运行时常量池的限制毫无意义。最终也只会抛出 java.lang.OutOfMemoryError: Java heap space 异常。


  1. 下面通过GCLib 模拟方法区溢出模拟的一个例子。


/**
 * -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m
 */
public class MyTest4 {
    public static void main(String[] args) {
        for (; ; ) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(MyTest4.class);
            enhancer.setUseCache(false);
            enhancer.setCallback((MethodInterceptor) (obj, method, args1, proxy) ->
                    proxy.invoke(obj, args1));
            System.out.println("hello world");
            enhancer.create();
        }
    }
}
//输出结果
Caused by: java.lang.OutOfMemoryError: Metaspace
  at java.lang.ClassLoader.defineClass1(Native Method)
  at java.lang.ClassLoader.defineClass(ClassLoader.java:756)
  ......


JVM 监控工具


VisualVM


VisualVM 提供在 Java 虚拟机 (Java Virutal Machine, JVM) 上运行的 Java 应用程序的详细信息。在 VisualVM 的图形用户界面中,可以方便、快捷地查看多个 Java 应用程序的相关信息。


image.png


参考资料


  1. 《深入理解 Java 虚拟机》 第三版 周志明


  1. 《Java 虚拟机规范(Java SE 8 版)》 爱飞翔 周志明 等译


  1. visualvm 主页文档


  1. Oracle 官网 Java 虚拟机规范


相关文章
|
4月前
|
Arthas 存储 算法
深入理解JVM,包含字节码文件,内存结构,垃圾回收,类的声明周期,类加载器
JVM全称是Java Virtual Machine-Java虚拟机JVM作用:本质上是一个运行在计算机上的程序,职责是运行Java字节码文件,编译为机器码交由计算机运行类的生命周期概述:类的生命周期描述了一个类加载,使用,卸载的整个过类的生命周期阶段:类的声明周期主要分为五个阶段:加载->连接->初始化->使用->卸载,其中连接中分为三个小阶段验证->准备->解析类加载器的定义:JVM提供类加载器给Java程序去获取类和接口字节码数据类加载器的作用:类加载器接受字节码文件。
399 55
|
5月前
|
Arthas 监控 Java
Arthas memory(查看 JVM 内存信息)
Arthas memory(查看 JVM 内存信息)
347 6
|
8月前
|
存储 设计模式 监控
快速定位并优化CPU 与 JVM 内存性能瓶颈
本文介绍了 Java 应用常见的 CPU & JVM 内存热点原因及优化思路。
875 166
|
10月前
|
缓存 Prometheus 监控
Elasticsearch集群JVM调优设置合适的堆内存大小
Elasticsearch集群JVM调优设置合适的堆内存大小
1640 1
|
6月前
|
存储 缓存 算法
JVM简介—1.Java内存区域
本文详细介绍了Java虚拟机运行时数据区的各个方面,包括其定义、类型(如程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区和直接内存)及其作用。文中还探讨了各版本内存区域的变化、直接内存的使用、从线程角度分析Java内存区域、堆与栈的区别、对象创建步骤、对象内存布局及访问定位,并通过实例说明了常见内存溢出问题的原因和表现形式。这些内容帮助开发者深入理解Java内存管理机制,优化应用程序性能并解决潜在的内存问题。
294 29
JVM简介—1.Java内存区域
|
6月前
|
缓存 监控 算法
JVM简介—2.垃圾回收器和内存分配策略
本文介绍了Java垃圾回收机制的多个方面,包括垃圾回收概述、对象存活判断、引用类型介绍、垃圾收集算法、垃圾收集器设计、具体垃圾回收器详情、Stop The World现象、内存分配与回收策略、新生代配置演示、内存泄漏和溢出问题以及JDK提供的相关工具。
JVM简介—2.垃圾回收器和内存分配策略
|
6月前
|
存储 设计模式 监控
如何快速定位并优化CPU 与 JVM 内存性能瓶颈?
如何快速定位并优化CPU 与 JVM 内存性能瓶颈?
137 0
如何快速定位并优化CPU 与 JVM 内存性能瓶颈?
|
7月前
|
存储 算法 Java
JVM: 内存、类与垃圾
分代收集算法将内存分为新生代和老年代,分别使用不同的垃圾回收算法。新生代对象使用复制算法,老年代对象使用标记-清除或标记-整理算法。
88 6
|
9月前
|
存储 Java 程序员
【JVM】——JVM运行机制、类加载机制、内存划分
JVM运行机制,堆栈,程序计数器,元数据区,JVM加载机制,双亲委派模型
186 10
|
9月前
|
存储 监控 算法
深入探索Java虚拟机(JVM)的内存管理机制
本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。