Java内存模型深度剖析

1、Java整体结构与内存模型

  • 堆空间
  • 元空间(方法区)
  • 程序计数器
  • 虚拟机栈
  • 本地方法栈
clipboard.png

1)虚拟机栈

一个线程对应一个虚拟机栈,虚拟机栈里存放的就是一个个栈帧,一个方法对应一个栈帧,每一个方法的调用过程对应着栈帧在虚拟机栈类入栈到出栈的过程

clipboard.png

栈帧结构

  • 局部变量表
  • 操作数栈
  • 动态链接
  • 方法出口

2)本地方法栈:本地方法信息,线程独有

3)元空间(方法区):线程共享

  • 运行时常量池
  • 类元信息
  • 静态变量

4)堆:保存对象和数组实例,线程共享

5)程序计数器:保存当前执行指令地址,线程独有

2、JVM内存参数设置

  • 最大堆空间:-Xmx
  • 初始堆空间:-Xms
  • 堆空间中新生代大小:-Xmn
  • 方法区最大空间:-XX:MaxMetaspaceSize
  • 方法区初始空间:-XX:MetaspaceSize
  • 虚拟机栈空间大小:-Xss
clipboard.png

Spring Boot程序的JVM参数设置格式(Tomcat启动直接加在bin目录下catalina.sh文件里):

java ‐Xms2048M ‐Xmx2048M ‐Xmn1024M ‐Xss512K ‐XX:MetaspaceSize=256M ‐XX:MaxMetaspaceSize=256M ‐jar microservice‐eurek a‐server.jar

3、逃逸分析

1)定义:Java Hotspot 虚拟机可以分析新创建对象的使用范围,并决定是否在 Java 堆上分配内存的一项技术。

  • 开启逃逸分析:-XX:+DoEscapeAnalysis
  • 关闭逃逸分析:-XX:-DoEscapeAnalysis
  • 显示分析结果:-XX:+PrintEscapeAnalysis

2)对象逃逸状态

  • 全局逃逸:当前一个对象作用范围逃出了当前方法或当前线程

  • 对象是一个静态变量

  • 对象是一个已经发生的逃逸对象

  • 对象作为当前方法的返回值

  • 参数逃逸:对象作为一个方法的参数传递或者被参数引用,但在调用过程中没有发生全局逃逸

  • 没有逃逸:对象的作用范围在方法内

3)逃逸分析优化

  • 锁消除:当编译器确定对象的作用范围只在当前线程使用,那么就会移除该对象上的同步锁

  • 标量替换

  • 标量:不能被进一步分解的量,比如基础类型和对象的引用

  • 聚合量:可以被进一步分解的量,比如对象

如果一个对象没有发生逃逸,那边它的作用域只有在当前方法类,那么JVM不会在在堆中创建对象,而是会在栈或寄存器上创建它用到的成员标量,这样在方法结束时这些成员标量直接回收了,不需要经历垃圾回收。

  • 开启标量替换:-XX:+EliminateAllocations

  • 关闭标量替换:-XX:-EliminateAllocations

  • 显示标量替换详情:-XX:+PrintEliminateAllocations

  • 栈上分配空间:当对象没有发生方法逃逸时,该对象可以通过标量替换方式将对象的成员属性分解成成员标量分配到栈内存中,和方法的生命周期一致,随着栈帧出栈时销毁,减少了gc压力,提高了应用程序的性能

参考:https://www.cnblogs.com/javastack/p/11023044.html

推荐阅读更多精彩内容