运行时数据区域:
包括 方法区,虚拟机栈,本地方法栈,堆 和程序计数器。
程序计数器:
是一块较小的内存空间,它的作用可以看作是当前线程所执行的字节码的行号指示器。
每一个线程都有自己私有的程序计数器。
如果线程正在执行的是一个JAVA方法,该计数器记录的是正在执行的虚拟机字节码指令的地址,如果正在执行的是native方法,则计数器值为空(undefined)。此内存区域是唯一一个在JAVA虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
JAVA虚拟机栈:
也是线程私有的,生命周期和线程相同。每个方法被执行的时候都会同时创建一个栈帧(stack frame)用于存储局部变量表、操作栈、动态链接、方法出口等信息。每个方法被调用直至执行完成的过程,就对应一个栈帧在虚拟机栈中从入栈道出栈的过程。
这个JAVA虚拟机栈就是我们常说的“栈”。局部变量表存放了元数据类型、引用类型
局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。
在JVM规范中,对这个区域规定了两种异常情况:
- 如果线程请求的栈深度大于JVM允许的深度,抛出StackOverflowError
- 如果虚拟机栈可以动态扩展(目前大部分JVM都可动态扩展),当扩展时无法申请到足够的内存时,会抛出OutOfMemoryError异常
本地方法栈:
与虚拟机栈作用类似,只不过前者是执行JAVA方法服务,而本地方法栈是为Native方法服务。
HotSpot将本地方法栈和虚拟机栈合二为一。
和虚拟机栈一样会抛出来两种异常。
JAVA堆:
JAVA堆是被所有线程共享的一块内存区域,在JVM启动时创建。其作用就是存放对象实例。
JVM规范规定:所有的对象实例及数组都要在堆上分配。
JAVA堆也是垃圾回收管理的主要区域。
由于现在收集器基本采用分代收集算法,所以JAVA堆还可以细分为:新生代和老年代,再细分还有Eden空间、From Survivor空间、To Survivor空间。
根据JVM规范,JAVA堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,可以是固定大小的,也可以是可扩展的(用-Xmx和-Xms控制),如果堆中没有内存,也无法扩展,抛出OutOfMemoryError。
方法区:
也是所有线程共享的内存区域。它用于存放虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
在JVM规范中被描述为堆的一部分,但却又有一个non-heap的别名。
对于HOTSPOT虚拟机,方法区又被称为永久代(Permanent Generation)
这个区域一样不需要连续的内存,可以选择固定大小或者扩展,还可以选择不实现垃圾回收。确实这个区域的数据一般不参与回收,但这些数据并不一定就是永久存在了,常量池和对类型的卸载也可以成为回收的目标。
抛出OutOfMemoryError
运行时常量池:
是方法区的一部分。
在编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。
这个区域并不是只有编译时产生的常量,也有运行时产生的常量,比如String.intern()方法。
OutOfMemoryError异常
直接内存:
Direct Memory
在JDK1.4中引入了NIO,是一种基于通道与缓冲区的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在JAVA堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。
抛OutOfMemoryError异常
Object obj = new Object();
Object obj反映到JAVA栈的本地变量表中
new Object()反映到JAVA堆中,长度不确定,在JAVA堆中还必须包含能查找到此对象类型数据(如对象类型、父类、实现的接口、方法等)的地址信息,这些类型数据则存放在方法区中。
引用访问对象的方法主流的有两种:
- 使用句柄访问方式,JAVA堆中将会划分出一块内存来作为句柄池,引用对象存储的是对象的句柄地址,而句柄中包含着对象实例数据和类型数据各自的具体地址信息。
- 直接指针访问方式。引用对象中直接存储的就是对象地址。,在堆中的对象又有一个指针指向方法区的对象类型数据。
句柄访问方式最大的好处是在对象被移动时(垃圾回收经常会移动对象),只用改变句柄中的实例数据指针,而引用对象不用修改。
直接指针方式最大的好处是速度更快。HOTSPOT使用这种方式。
设置堆不可扩展,将-Xms和-Xmx参数设置一样即可。
栈溢出
-Xoss参数设置本地方法栈大小(在HOTSPOT虚拟机中,由于本地方法栈和虚拟机栈是一起的,所以这个参数是无效的),-Xss参数设定栈大小。
栈溢出会抛出两种异常stackoverflow和outofmemory,前者表示线程请求的栈深度大于虚拟机所允许的最大深度,后者表示无法申请到足够的内存空间。
实验表明,单线程只会有stackoverflow,不会有outofmemory.但多线程会产生Outofmemory。
这是因为,OS分配给每个进程的内存是有限制的,比如32位windows限制为2GB,栈内存的总量是由2GB减去堆内存(Xmx),再减去方法区容量(MaxPermSize),因为程序计数器消耗内存很小,可以忽略不计。而栈内存又被多个线程所瓜分,这样在线程很多的情况下,内存就可能会耗尽。
运行时常量池溢出
通过String.intern()方法可以向运行时常量池增加内容,该方法会检测常量池中是否有某个字符串,如果有就返回池中的字符串,否则将该字符串加入常量池,再返回该常量池字符串的引用。
由于常量池分配在方法区中,可以通过-XX:PermSize和-XX:MaxPermSize来限制方法区大小。方法区溢出的标志就是OutOfMemoryError: PermGen Space.
方法区溢出
主要是由于动态生成了太多的类造成的,这种溢出的实际场景比如大量JSP或动态生成JSP文件的应用(JSP第一次运行时会需要编译成JAVA类)CGLIB等动态代理生成框架产生的动态类等
本机直接内存溢出
DirectMemory容量可以由-XX:MaxDirectMemorySize指定,如果不指定,则默认与JAVA堆最大值(-Xmx)一样。实际上DirectByteBuffer并没有直接申请分配内存,而是先计算,如果计算得知无足够内存可分配,就抛出异常。
相关推荐
读书笔记:《深入理解Java虚拟机_JVM高级特性与最佳实践 第2版》
Java 虚拟机(JVM)自动内存管理机制 Java 虚拟机(JVM)自动内存管理机制是 Java 语言的一大特色,它使得 Java 程序员无需手动管理内存,从而提高了开发效率和程序稳定性。JVM 自动内存管理机制主要通过 JVM 的...
《深入理解Java虚拟机》是Java开发者必读的经典之作,其中第三章主要探讨了Java安全方面的内容。在Java中,安全是一个至关重要的概念,因为Java的设计目标之一就是提供一种可以在不同环境中安全运行的代码机制。本章...
读书笔记:学习周志明先生的深入理解Java虚拟机的笔记
自己看《深入理解Java虚拟机》(第二版)所做的一些笔记。因为个人水平有限,能够理解的也只有前面几章的内容,后面的内容觉得看了也不是很理解,就没有记在里面。希望能对大家有所帮助,也希望能和大家一起进步。
### 学习深入理解Java虚拟机的前几章笔记 #### JVM内存模型 Java虚拟机(JVM)的内存模型主要分为两大类:线程共享区和线程私有区。 ##### 线程共享区 - **堆**:是所有线程共享的内存区域,在这里存放着对象实例...
读书笔记:深入理解Java虚拟机第二版_周志明读书笔记
总的来说,"深入理解Java虚拟机读书笔记之:第3章 安全(2)"主要涵盖了Java安全体系的核心概念,包括类加载器、权限模型、安全管理器以及相关工具的使用。理解这些内容对于任何希望构建安全、可靠的Java应用程序的...
深入理解 Java 虚拟机笔记 Java 虚拟机(JVM)是 Java 语言的运行环境,它负责解释和执行 Java 字节码。下面是 Java 虚拟机相关的知识点: 虚拟机内存结构 Java 虚拟机的内存结构主要包括以下几个部分: * 方法...
读书笔记:周志明的《深入理解java虚拟机》读书笔记
读书笔记:java 虚拟机,深入理解Java虚拟机 JVM高级特性与最佳实践
读书笔记:深入理解Java虚拟机周志明
读书笔记:深入理解Java虚拟机周志明著
读书笔记:深入理解Java虚拟机(周志明)源码及学习笔记
读书笔记:周志明老师《深入理解Java虚拟机》第三版阅读笔记
读书笔记:深入理解Java虚拟机JVM高级特性与最佳实践
读书笔记:深入理解Java虚拟机 JVM高级特性与最佳实践
读书笔记:深入理解Java虚拟机JVM高级特性与最佳实践第3版学习笔记
读书笔记:深入理解Java虚拟机 JVM高级特性与最佳实践第二章笔记