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 虚拟机规范


相关文章
|
1月前
|
存储 设计模式 监控
快速定位并优化CPU 与 JVM 内存性能瓶颈
本文介绍了 Java 应用常见的 CPU & JVM 内存热点原因及优化思路。
590 166
|
3月前
|
缓存 Prometheus 监控
Elasticsearch集群JVM调优设置合适的堆内存大小
Elasticsearch集群JVM调优设置合适的堆内存大小
692 1
|
4月前
|
存储 安全 Java
jvm 锁的 膨胀过程?锁内存怎么变化的
【10月更文挑战第3天】在Java虚拟机(JVM)中,`synchronized`关键字用于实现同步,确保多个线程在访问共享资源时的一致性和线程安全。JVM对`synchronized`进行了优化,以适应不同的竞争场景,这种优化主要体现在锁的膨胀过程,即从偏向锁到轻量级锁,再到重量级锁的转变。下面我们将详细介绍这一过程以及锁在内存中的变化。
57 4
|
6天前
|
存储 算法 Java
JVM: 内存、类与垃圾
分代收集算法将内存分为新生代和老年代,分别使用不同的垃圾回收算法。新生代对象使用复制算法,老年代对象使用标记-清除或标记-整理算法。
17 3
|
2月前
|
存储 Java 程序员
【JVM】——JVM运行机制、类加载机制、内存划分
JVM运行机制,堆栈,程序计数器,元数据区,JVM加载机制,双亲委派模型
|
2月前
|
存储 监控 算法
深入探索Java虚拟机(JVM)的内存管理机制
本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。
|
3月前
|
Java
JVM内存参数
-Xmx[]:堆空间最大内存 -Xms[]:堆空间最小内存,一般设置成跟堆空间最大内存一样的 -Xmn[]:新生代的最大内存 -xx[use 垃圾回收器名称]:指定垃圾回收器 -xss:设置单个线程栈大小 一般设堆空间为最大可用物理地址的百分之80
|
3月前
|
Java
JVM运行时数据区(内存结构)
1)虚拟机栈:每次调用方法都会在虚拟机栈中产生一个栈帧,每个栈帧中都有方法的参数、局部变量、方法出口等信息,方法执行完毕后释放栈帧 (2)本地方法栈:为native修饰的本地方法提供的空间,在HotSpot中与虚拟机合二为一 (3)程序计数器:保存指令执行的地址,方便线程切回后能继续执行代码
40 3
|
3月前
|
存储 缓存 监控
Elasticsearch集群JVM调优堆外内存
Elasticsearch集群JVM调优堆外内存
75 1
|
3月前
|
Arthas 监控 Java
JVM进阶调优系列(9)大厂面试官:内存溢出几种?能否现场演示一下?| 面试就那点事
本文介绍了JVM内存溢出(OOM)的四种类型:堆内存、栈内存、元数据区和直接内存溢出。每种类型通过示例代码演示了如何触发OOM,并分析了其原因。文章还提供了如何使用JVM命令工具(如jmap、jhat、GCeasy、Arthas等)分析和定位内存溢出问题的方法。最后,强调了合理设置JVM参数和及时回收内存的重要性。