写在前面: 虚拟机自动内存管理的机制下,不需要为每个new操作取写配对的delete/free代码,也不易出现内存泄漏和内存溢出问题。也正是因为把控制内存的权力交给了java虚拟机,一旦出现内存泄漏,后果很严重。因此需要了解虚拟机是怎样使用内存的。
Java虚拟机内存
运行时数据区
-
程序计数器(线程私有)
-
一块较小的内存空间,可以看做是当前线程执行的字节码行号的指示器。它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复都依赖计数器来完成。
为什么是线程私有? java多线程的虚拟机是通过线程轮流切换、分配处理器执行时间的方式实现,因此,在切换线程时需要恢复正确的位置,所以每个线程都有一个独立的程序计数器,线程之间计数器互不影响。
执行java方法,计数器记录的是正在执行的虚拟机字节码指令的地址;
执行本地方法,计数器为undefined。
没有任何OutOfMemoryError的区域
生命周期与线程相同 -
虚拟机栈(线程私有)
生命周期与线程相同,每个方法创建时都会创建一个栈帧用于存储局部变量表、操作数栈、动态连接、方法出口等信息。
局部变量存放编译期可知的各种java虚拟机基本数据类型。
线程请求的栈深度大于虚拟机所允许的深度,抛Stack Overflow Error
栈扩展时无法申请到足够内存会抛出out of memoryError -
本地方法栈(线程私有)
本地方法栈与虚拟机栈类似,唯一不同的是两者执行的服务不同,前者为虚拟机使用的本地方法服务,而后者是为虚拟机执行Java方法服务。
有的java虚拟机会把本地方法栈和虚拟机栈合二为一。
在栈深度溢出或扩展失败时分别抛出Stack Overflow Error 或out of memoryError -
堆
虚拟机管理内存中最大的一块,java堆是被所有线程共享的一块内存区域,虚拟机启动时创建。
用途:存放对象,几乎 所有对象都在这里分配内存。随着java逃逸分析技术 的发展,有的对象也可能在栈中分配。
java堆是垃圾收集器管理的内存区域。也叫“GC”堆,java堆中没有完成内存实例分配且堆无法扩展时抛出OutOfMemoryError.
逃逸分析技术:此处未作详细说明,感兴趣请参考如下这篇文章:https://blog.csdn.net/w372426096/article/details/80938788
-
方法区
线程共享。
用途:用来存储已被类加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
别名 非堆,与java堆区分。
一个变动:JDK7的HotSpot已经把原本放在永久代的字符串常量池、静态变量等移出。到 JDK8完全废弃了永久代的概念,改用元空间 代替
方法区无法满足新的内存需求分配时抛出OutOfMemoryError- 运行时常量池
方法区的一部分。用于存放编译期生成的各种字面量与符号引用,在类加载后放到方法去的运行时常量池中。
- 运行时常量池
-
其他
直接内存:并不是虚拟机运行时的一部分
NIO:基于通道缓冲区的I/O方式,使用Native函数库直接分配堆外内存。