内存管理是每种开发语言不可避免需要考虑的问题,而JAVA语言作为主流的开发语言,其内存管理机制也是JAVA开发人员不可避免需要学习的内容,与C++不同的是,JAVA是将内存管理的控制权交给了JVM(java虚拟机),由其自动管理内存分配和回收,而不再需要开发人员手动的进行内存的分配和释放,故了解JVM运行时的内存机制有助于在程序发生内存溢出或者泄露时的问题快速定位。
一、JVM运行时数据区划分
JVM的运行时数据区主要划分为以下几个模块:
1、程序计数器:程序计数器是分配的一块比较小的内存空间,是线程私有的,我们知道JAVA源程序(.java文件)在被JVM执行前需要被编译成字节码(.class文件),而程序计算器可以认为是当前线程所执行的字节码的行号指示器,主要用于在遇到控制流程(如分支、循环、跳转、异常处理等)时能准确地回到之前执行的位置继续执行。
2、虚拟机栈:虚拟机栈和程序计数器一样也是线程私有的,它的生命周期与线程相同,是java方法执行的线程内存模型,在每个java方法被执行的时候,JVM都会同步创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口信息等,每个方法被调用执行到执行完毕的过程,对应于一个栈帧在虚拟机栈中入栈到出栈的过程。
3、本地方法栈:本地方法栈与虚拟机栈类似,区别在于虚拟机栈执行的是java方法,而本地方法栈执行的是本地(Native)方法。
4、JAVA堆:Java堆是JVM管理的最大的一块内存对象,他是所有线程共享的,其主要存储的内容就是对象实例,当然,堆上也可以划分出线程私有的分配缓冲区(Tread Local Allocation Buffer,TLAB),用于提升对象分配的效率,java堆内存的大小可以通过-Xmx(最大可用堆内存)和-Xms(初始可用堆内存)参数来设置(一般设置成相同值避免垃圾回收后的内存再分配)。
5、方法区:方法区和堆一样是所有线程共享的一块区域,主要用于存储JVM加载的类型信息、常量、静态变量等。
6、运行时常量池:是方法区的一部分,.class文件中除了有类的版本、字段、方法、接口等描述信息外,还有就是常量池表用于存储编译时生成的各种字面量和符号引用,这部分内容就存放在运行时常量池中。
7、直接内存:直接内存不是JVM运行时数据区的一部分,但是这部分内存也会被频繁的使用,它是JDK1.4新加入的NIO(New Input/Output)类引入的一种基于通道(Channel)与缓存区(Buffer)的I/O方式,这种方式可以使用Native函数直接分配对外内存,并通过在堆内DirectByteBuffer对象持有分配的对象的引用来访问对象,因为避免了Java堆和Native堆的来回复制对象,因而能显著的提高性能。
以上七个模块除了程序计数器外,都有可能在内存分配时,因直接分配或动态扩展时无法获取内存空间而发生OutOfMemoryError异常。
来源 : https://zhuanlan.zhihu.com/p/670046584?utm_id=0