JVM 内存模型概述
Posted 后端初学者
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM 内存模型概述相关的知识,希望对你有一定的参考价值。
每天一篇好文章系列18年第224期
Java程序在执行前首先会被编译成字节码文件,然后再由Java虚拟机执行这些字节码文件从而使得Java程序得以执行。事实上,在程序执行过程中,内存的使用和管理一直是值得关注的问题。本文探讨了在虚拟机中对象的创建和对象的访问定位等问题,并分析了Java虚拟机规范中异常产生的情况。
1
1.Java 虚拟机内存模型
Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,这些数据区域可以分为两个部分:一部分是线程共享的,一部分则是线程私有的。其中,线程共享的数据区包括方法区和堆,线程私有的数据区包括虚拟机栈、本地方法栈和程序计数器。
线程私有数据区:
程序计数器:线程私有的一块较小的内存空间,其可以看做是当前线程所执行的字节码的行号指示器。
虚拟机栈:描述的是Java方法执行的内存模型,是线程私有的。每个方法在执行的时候都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息,而且 每个方法从调用直至完成的过程,对应一个栈帧在虚拟机栈中入栈到出栈的过程。
本地方法栈:与Java虚拟机栈非常相似,也是线程私有的,区别是虚拟机栈为虚拟机执行 Java 方法服务,而本地方法栈为虚拟机执行 Native 方法服务。
线程共享的数据区:
Java 堆:它的唯一目的就是存放对象实例,几乎所有的对象实例(和数组)都在这里分配内存。Java堆是线程共享的,类的对象从中分配空间,这些对象通过new、newarray、anewarray 和 multianewarray 等指令建立,它们不需要程序代码来显式的释放。
方法区:与Java堆一样,也是线程共享的并且不需要连续的内存,其用于存储已被虚拟机加载的 类信息、常量、静态变量、即时编译器编译后的代码等数据。
02
2.Java对象在虚拟机中的创建与访问定位
对象在虚拟机中的创建过程
(1)检查虚拟机是否加载了所要new的类,若没加载,则首先执行相应的类加载过程。
(2)在类加载检查通过后,对象所需内存的大小在类加载完成后便可完全确定,虚拟机就会为新生对象分配内存。
(3)内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值。
(4)在上面的工作完成之后,从虚拟机的角度来看,一个新的对象已经产生了,但从Java程序的视角来看,对象的创建才刚刚开始,此时会执行<init>方法把对象按照程序员的意愿进行初始化,从而产生一个真正可用的对象。
对象在虚拟机中的访问定位
创建对象是为了使用对象,我们的Java程序通过栈上的reference数据来操作堆上的具体对象。在虚拟机规范中,reference类型中只规定了一个指向对象的引用,并没有定义这个引用使用什么方式去定位、访问堆中的对象的具体位置。目前的主流的访问方式有使用句柄访问和直接指针访问两种。
03
3.内存异常产生情况分析
Java堆溢出 (OOM)
Java堆用于存储对象的实例,只要不断地创建对象,并且保证GC roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,那么在对象数量到达最大堆的容量限制后就会产生内存溢出异常。
虚拟机栈和本地方法栈溢出 (SOF/OOM)
SOF:如果线程请求的栈深度大于虚拟机栈允许的最大深度,将抛出StackOverflowError异常。
OOM:如果虚拟机在拓展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。
方法区和运行时常量池溢出 (OOM)
String.intern()是一个native方法,在JDK1.6及之前的版本中,它的作用是:如果字符串常量池中已经包含一个等于此String对象的字符串,则返回代表池中这个字符串的String对象,否则将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用。由于常量池分配在永久代中,如果不断地使用intern方法手动入池字符串,则会抛出OutOfMemoryError异常。但在JDK1.7及其以后的版本中,对intern()方法的实现作了进一步改进,其不会再复制实例到常量池中,而仅仅是在常量池中记录首次出现的实例的引用。
更多详细信息,请点击左下角原文链接查看
点评:作为Java开发人员来说,不需要像C/C++开发人员那样,需要时刻注意内存的释放,而是全权交给虚拟机去管理。但是,了解虚拟机的运行内存模型,更有助于我们从底层进行性能调优。
本文转自
CSDN博客《JVM 内存模型概述》
相关阅读推荐
深入了解请点击下方阅读原文
以上是关于JVM 内存模型概述的主要内容,如果未能解决你的问题,请参考以下文章