赞
踩
JVM 内存结构,也就是运行时数据区,它主要包含5个部分:
Program Counter Register
用来存储执行下一条要执行的指令的地址,也就是将要执行的指令代码, 由执行引擎读取下一条指令。
它是一块很小的内存空间,小到可以忽略不计。
任何时间,一个线程只有一个方法在执行,叫做当前方法。程序计数器会存储当前线程执行的方法的JVM指令地址;或者如果执行的是native方法,则是未指定值(undefined)
使用PC寄存器存储字节码指令地址有什么用?
CPU会不停地切换各个线程,再切换回来的时候,需要通过它来确定此线程接下来执行哪条指令
本地方法栈用于管理本地方法(native)的调用,本地方法时C语言实现的,本地方法中用到的变量存放在本地方法栈对于的内存空间中。
一般情况下,不需要关注。
栈帧的内部结构
局部变量表
slot(槽)
操作数栈
栈顶缓存技术:
基于栈式架构的虚拟机所使用的【零地址指令】更加紧凑,但是每个操作都有入栈和出栈,就意味着需要更多的指令分派次数和内存读写次数,会影响执行速度。
为了解决上面的问题,HotSpot JVM 设计了栈顶缓存技术,将栈顶元素全部缓存到物理CPU的寄存器(寄存器特点:指令更少,执行速度快)中,以此降低对内存的读写次数。
动态链接
每个栈帧内部都包含一个指向运行时常量池中该栈帧所属方法的引用(上图中橘黄色和绿色部分)
在java源文件被编译到字节码文件中时,所有的变量和方法引用都作为【符号引用】保存在class文件的常量池中。比如描述一个方法调用了另外的其他方法时,就是通过常量池中指向方法的符号引用来表示的。那么动态链接的作用就是:将这些符号引用转换为调用方法的直接引用
常量池作用:为了提供一些符号和常量,便于指令的识别
方法出口
Java 中的堆是 JVM 所管理的最大的一块内存空间,也是最重要的内存空间之一。主要用于存放各种类的实例对象。
java 堆分为新生代(Young)和老年代(old)。
新生代:
生命周期较短,创建和消亡都非常迅速
新生代又分为三个区域:Eden、From Survivor ( s0 )、To Survivor ( s1 )。
老年代:
生命周期非常长
堆空间相关参数:
对象逃逸分析:
在Java虚拟机中,对象是在Java堆中分配内存的,这是一个普遍的常识,但是有一种特殊情况。那就是如果经过逃逸分析,后发现,一个对象并没有逃逸出方法的话,那么就可能被优化成栈上分配。这样就无须再堆上分配了,也不用进行垃圾回收了。这也是最常见的堆外存储技术。
解释对象逃逸分析前,先看看JVM的三种运行模式:
混合模式是JVM默认采用的执行代码方式,一开始还是解释执行,但是对于少部分“热点”代码会采用编译模式执行,这些热点代码对应的机器码会被缓存起来,下次再执行无需再编译,这就是我们常见的JIT(Just In Time Compiler)即时编译技术。 在即时编译过程中JVM可能会对我们的代码做一些优化,比如对象逃逸分析等。
public void test() {
User user = new User();
user.setId(1);
user.setName("zhuge");
// TODO 保存到数据库
}
对象逃逸分析:上面test方法中的user对象,我们可以确定当方法结束这个对象就可以认为是无效对象了。对于这样的对象我们其实可以将其分配的栈内存里,让其在方法结束时跟随栈内存一起被回收掉,不过这个也只是可能在栈上分配,而不是绝对!
如果一个对象被发现只能从一个线程访问,那么对这个对象的操作可以不考虑同步。
JVM对于这种情况可以通过开启逃逸分析参数(-XX:+DoEscapeAnalysis)来优化对象内存分配位置,jdk7 之后默认开启逃逸分析,如果要关闭使用参数(-XX:-DoEscapeAnalysis)
方法区是逻辑上的东西,是JVM的规范,所有虚拟机必须遵守。
方法区是 JVM 所有线程共享的、用于存储类的信息、常量池、方法数据、方法代码等。
jdk版本 | jdk6 | jdk7 | jdk8 |
---|---|---|---|
方法区实现 | 永久代 | 永久代 | 元空间 |
运行时常量池位置 | 永久代 | 永久代 | 元空间 |
字符串常量池位置 StringTable | 永久代 | 堆 (Heap) | 堆 (Heap) |
静态变量位置 | 永久代 | 堆 (Heap) | 堆 (Heap) |
类的类型,方法,域信息 | 永久代 | 永久代 | 元空间 |
jdk8 方法区:
元空间与永久代之间最大的区别
元空间并不在虚拟机中,而是使用本地内存。
为什么要移除永久代?
方法区主要存储类的相关信息,类及方法的信息等比较难确定其大小,而且有的业务功能点比较多,在运行过程中,要不断加载很多类,可能就OOM了。而元空间并不在虚拟机内存中,而是使用本地内存,因此默认情况下,元空间大小仅收本地内存限制。
以 jdk8 为例:
java -jar 启动时指定 jvm 参数:
java ‐Xms2048M ‐Xmx2048M ‐Xmn1024M ‐Xss1024K ‐XX:MetaspaceSize=512M ‐XX:MaxMetaspaceSize=512M ‐jar xxxxxx.jar
tomcat 中指定 jvm 参数:catalina.sh 的 JAVA_OPTS 中指定。
-Xss 设置越小,一个线程栈里能分配的栈帧就越少,但是对JVM整体来说能开启的线程数会更多。
当然,还有很多其他参数,需要根据应用的实际情况考虑来考虑如何设置。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。