Java虚拟机
Posted swanspouse
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java虚拟机相关的知识,希望对你有一定的参考价值。
Java内存区域
运行时数据区域
程序计数器(Program Counter Register)
- 较小的内存空间,是当前线程执行的字节码的行号的指示器。字节码解释器通过改变计数器的值来获取下一条字节码指令,分支、循环、跳转、异常处理、线程恢复都靠它完成.
- 每个线程都有自己的一个计数器,线程之间计数器互不影响.
- 执行Native方法时,计数器不起作用,值为空(Undefined)。
- 此区域是唯一没有规定OOM的区域.
Java虚拟机栈 Java Virtual Machine Stack
- 归属线程所有,生命周期与归属的线程相同。
- 虚拟机栈描述的是Java方法执行的内存模型:
- 方法执行时会创建栈帧(Stack Frame)存储局部变量表、操作数栈、动态链接、方法出口等.
- 方法的调用到执行完成的过程,对应其栈帧从虚拟机栈中入栈到出栈的过程.
- 局部变量表存放编译器可知的基本数据类型、对象引用;long、double因为长度为64bit,会占用两个Slot,其他则占用一个,由此可知局部变量表的内存空间在编译期就完成了分配,方法运行时不改变其大小。
- 虚拟机栈中规定的两种异常情况:
- StackOverflowError:线程请求的栈深度大于虚拟机所允许的深度.
- OutOfMemoryError:虚拟机栈动态扩展内存时,无法申请到足够的内存.
本地方法栈 Native Method Stack
- 类似于虚拟机栈,区别是虚拟机栈为虚拟机执行Java方法服务,而本地方法栈为Native方法工作;Native方法指Java调用的C/C++语言的函数.
- 本地方法栈区域也会抛出StackOverFlowError & OutOfMemoryError异常。
Java堆 Java Heap
- JVM中内存最大的一块,所有线程共享,在虚拟机启动时创建。几乎所有对象实例、数组都在这里诞生。
- Java堆是垃圾回收的主要区域,又称GC(Garbage Collected Heap)堆。
- 当堆中没有内存分配实例,并且无法扩展时,抛出OOM异常。
Java堆可以处于物理上的不连续的内存空间中,只要逻辑上是连续的即可。
堆空间的内存细分:
- Young Generation:新生代,包含了Eden、S0(FromSurvivor)、S1(ToSurvivor);由于新生代上的对象存活时间很短暂,所以此区一般采用标记-清理算法GC.
- Old Generation:老年代,经过多次GC后仍然存活的对象(每个对象都有一个对象年龄计数器),此区域采用一般采用复制算法GC;大对象,如数组,直接进入老年代.
- Permanent:永久代,存放不变对象,类、方法、字符串等,见以下方法区。
- 堆内存的分代回收:
- 大多数刚创建的对象会被分配在Eden区,其中的大多数对象很快就会消亡。Eden区是连续的内存空间,因此在其上分配内存极快.
- 当Eden区满的时候,执行Minor GC,将消亡的对象清理掉,并将剩余的对象复制到一个存活区Survivor0(此时,Survivor1是空白的,两个Survivor总有一个是空白的).
- 此后,每次Eden区满了,就执行一次Minor GC,并将剩余的对象都添加到Survivor0.
- 当Survivor0也满的时候,将其中仍然活着的对象直接复制到Survivor1,以后Eden区执行Minor GC后,就将剩余的对象添加Survivor1(此时,Survivor0是空白的).
- 当两个存活区切换了几次(HotSpot虚拟机默认15次,用-XX:MaxTenuringThreshold控制,大于该值进入老年代)之后,仍然存活的对象(其实只有一小部分,比如,我们自己定义的对象),将被复制到老年代.
- 总结:Eden区是连续的空间,且Survivor总有一个为空;经过一次GC和复制,一个Survivor中保存着当前还活 着的对象,而Eden区和另一个Survivor区的内容都不再需要了,可以直接清空,到下一次GC时,两个Survivor的角色再互换。因此,这种方 式分配内存和清理内存的效率都极高,这种垃圾回收的方式就是著名的“停止-复制(Stop-and-copy)”清理法(将Eden区和一个Survivor中仍然存活的对象拷贝到另一个Survivor中).
方法区 Method Area
- 所有线程共享,别名No-Heap(非堆)用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。也叫Permanent Generation(永久代);之所以名为永久代,是因为HotSpot将GC从堆延伸至了方法区。(对于其他虚拟机IBM J9等来说是不存在永久代的概念的)
- 与Java堆一样,是各个线程共享的内存区域,它用于存储 已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
- 并非被称为永久代,其中的数据就永久了,当类型卸载、常量池回收时也会发生GC,但是比较少见。
- 当此区无法分配新内存时,抛出OOM异常。
运行时常量池 Runtime Constant Pool
- 属于方法区的一部分,Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。
- 无法申请到内存时,抛出OOM异常.
直接内存
- 直接内存不是JVM运行时的一部分,但是Java可以通过NIO(New Input/Output),引入Channel和Buffer的I/O方式,它可以用native方法申请堆外内存,然后通过JVM堆中的DirectByteBuffer对象操作这块内存,此时申请内存是通过Unsafe.allocateMemory方法实现的.
对象访问
- 句柄访问方式:Java堆中分配一块内存作为句柄池,reference中存储对象的句柄地址,句柄中则包含实例数据指针和类型数据指针;此方式的好处在于句柄地址稳定,对象变动时,只需修改句柄,而reference不需改动.
- 直接指针访问方式:Java堆对象中存放访问类型数据信息,reference直接存储对象地址;这种做法的好处是速度快,节省了一次指针定位的开销.
以上是关于Java虚拟机的主要内容,如果未能解决你的问题,请参考以下文章