本文主要讲java的运行时数据区域。
Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。如图:
描述: 程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。
作用: 它是程序控制流的指示器,分支,循环,跳转,异常处理,线程恢复 等基础功能都需要依赖这个计数器来完成。
线程私有的内存: 每一条线程都需要一个独立的程序计数器。
原因: java虚拟机的多线程是通过线程轮流切换、分配处理器执行时间的方式来实现的。在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令。
注意:
描述: 虚拟机栈描述的是Java方法执行的线程内存模型:每个方法被执行得时候,Java虚拟机都会同步创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
方法的执行就对应着栈帧在虚拟机栈中入栈和出栈的过程;
且这些数据类型在局部变量表的存储空间以局部变量槽(Slot)来表示。64位长度的long, 和double占用两个变量槽,其他为一个。
描述: 本地方法栈是为虚拟机使用到的本地(Native)方法服务。
摘抄一段话,下面有翻译。
Any native method interface will use some kind of native method stack. When a thread invokes a Java method, the virtual machine creates a new frame and pushes it onto the Java stack. When a thread invokes a native method, however, that thread leaves the Java stack behind. Instead of pushing a new frame onto the threadís Java stack, the Java Virtual Machine will simply dynamically link to and directly invoke the native method. One way to think of it is that the Java Virtual Machine is dynamically extending itself with native code. It is as if the Java Virtual Machine implementation is just calling another (dynamically linked) method within itself, at the behest of the running Java program.
任何本地方法接口都会使用某种本地方法栈。当线程调用Java方法时,虚拟机会创建一个新的栈帧并压入java栈。然而当他调用的是本地方法时,虚拟机会保持Java栈不变 ,不再在线程的java栈中压入新的帧,虚拟机只是简单地动态连接并直接调用指定的本地方法。可以把这看做是虚拟机利用本地方法来动态扩展自己 。就如同Java虚拟机的实现在按照其中运行的Java程序的吩咐,调用属于虚拟机内部的另一个(动态连接的)方法。
原文链接:JVM学习笔记-本地方法栈(Native Method Stacks)
描述: java堆是被所有线程共享的一块内存区域,在虚拟机启动是创建。
作用: 唯一目的是存放对象实例,java世界里“几乎”所有的对象都在这里分配内存。
java堆细分: java堆是垃圾收集器的管理的内存区域。不少资料因垃圾回收机制,划分了新生代,老年代,永久代,Eden, From Survivor ,To Survivor等。时代发展,概念不一定完全对的上,但是将java堆细分的目的都是为了更好地回收内存,或者更快地分配内存。
操作: java堆可以被实现成固定大小,也可以可扩展。
通过参数 -Xmx 和Xms设定。
具体eclipse设置其大小,点这链接 用eclipse运行项目时怎么设置虚拟机内存大小
注意: java堆可以处于物理上不连续的内存空间,但逻辑上应是连续的。
描述: 和java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。 别名:非堆。
操作: 它不需要连续空间,可以固定大小和可扩展,甚至可以选择不实现垃圾收集。
注意:
描述: 运行时常量池是方法区的一部分。
Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项是常量池表,用于存放编译期生成得各种字面量和符号引用,这部分内存将在类加载后存放到方法区的运行常量池中。
注意:
描述: 直接内存并不是虚拟机运行时数据区的一部分,也不是《java虚拟机规范》中定义的内存区域。但是这部分内存也被频繁地使用。
在JDK 1.4中新加入NIO(New Input/ Output)类,引用了一种基于通道与缓存区的I/O方式,它可以使用Native函数库直接分配对外内存,然后通过一个存储在java堆里面的DirectBytebuffer对象作为这块内存的引用进行操作。
这样能在一些场景中显著提高性能,因为避免了在java堆和Native堆中来回复制数据。
注意: 该内存不受Java堆大小限制,但会受到本机总内存大小及处理器寻址空间的限制。
可以通过MaxDirectMemorySize来设置( 默认与堆内存最大值一样) , 所以也会出现OOM异常。
不同版本下内存图的区别:
本文主要是对《深入理解Java虚拟机》一书第二章有关Java内存区域的读书笔记与点点记录。
优秀其他文章:Java内存区域划分
笔者水平有限,目前只能描述以上问题,如果有其他情况,可以留言,有错误,请指教,有继续优化的,请分享,谢谢!
本篇文章是否有所收获?阅读是否舒服?又什么改进建议?希望可以给我留言或私信,您的的分享,就是我的进步。谢谢。
2020.9.10 网安院2楼