转载:
原文链接:https://blog.csdn.net/qq906627950/article/details/81324825
JVM所管理的内存包括以下几个运行时数据区域,如图所示
方法区和堆为线程共享区,虚拟机栈、本地方法栈及程序计数器为线程独占区。
程序计数器是一块较小的空间,它可以看作是当前线程所执行的字节码的行号指示器。
如果线程执行的是java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址(可以理解为上图所示的行号),如果正在执行的是native方法,这个计数器的值为undefined。
JVM的多线程是通过线程轮流切换并分配CPU执行时间片的方式来实现的,任何一个时刻,一个CPU都只会执行一条线程中的指令。为了保证线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各线程间的程序计数器独立存储,互不影响。
此区域是唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError情况的区域,因为程序计数器是由虚拟机内部维护的,不需要开发者进行操作。
每个线程有一个私有的栈,随着线程的创建而创建,生命周期与线程相同。
方法的调用到执行完毕,对应的就是栈帧的入栈和出栈的过程。
栈的大小可以固定也可以动态扩展。
在固定大小的情况下,当栈调用深度大于JVM所允许的范围,会抛出StackOverflowError异常。
在动态扩展的情况下,若扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。
public class MainTest {
public static void main(String[] args){
new MainTest().test();
}
private void test() {
System.out.println("run...");
test();
}
}
Exception in thread "main" java.lang.StackOverflowError
at sun.nio.cs.UTF_8$Encoder.encodeLoop(UTF_8.java:691)
at java.nio.charset.CharsetEncoder.encode(CharsetEncoder.java:579)
和虚拟机栈类似,两者的区别就是虚拟机栈是为虚拟机执行java方法服务,本地方法栈为虚拟机执行native方法服务 。
HotSpot虚拟机不区分虚拟机栈和本地方法栈,两者是一块的。
与虚拟机栈一样,本地方法栈也会抛StackOverflowError和OutOfMemoryError异常。
JVM管理的最大的一块内存区域,存放着对象的实例,是线程共享区。
堆是垃圾收集器管理的主要区域,因此也被称为“GC堆”
JAVA堆的分类:
JAVA堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。
可通过参数 -Xmx -Xms 来指定运行时堆内存的大小,堆内存空间不足也会抛OutOfMemoryError异常
方法区也是线程共享区,用于存储【虚拟机加载的类信息(类的版本、字段、方法、接口),常量,静态变量,即时编译器编译后的代码等数据】
方法区逻辑上属于堆的一部分,但是为了与堆进行区分,通常又叫“非堆”。
HotSpot虚拟机使用永久代来实现方法区,使得HotSpot虚拟机的垃圾收集器可以像管理堆内存一样来管理这部分内存,能省去专门为方法区编写内存管理代码工作。所以开发者喜欢将方法区称为永久代,本质上两者并不等价,对于其他虚拟机来说不存在永久代的概念。
方法区可选择不实现垃圾收集,一般来说,这个区域对内存回收的条件较为苛刻,但是这部分区域的回收确实是必要的。
当方法区无法满足内存分配需求时,将会抛OutOfMemoryError异常
运行时常量池
一个对象的创建主要包括了一下几大流程
在JVM在堆中为对象分配内存阶段,通常有以下两种分配方式,虚拟机选择哪种分配方式是由JAVA堆是否规整决定的,而JAVA堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。
在为对象分配内存时,还需要考虑的一点就是线程安全性问题。可能出现正在给对象A分配内存,指针还没来得及修改,对象B又同时使用了原来的指针来分配内存的情况。针对这种问题,有以下两种解决方案。
在HotSpot虚拟机中,对象在内存中存储的布局可分为3块区域:对象头、实例数据和对齐填充。
Mark Word,用于存储对象自身的运行时数据,如哈希码、GC分代年龄、锁状态标志、线程持有锁、偏向线程ID、偏向时间戳等。
类型指针,虚拟机通过这个指针来确定对象是哪个类的实例,该指针指向对象的类元数据。(由于查找对象的元数据信息并不一定要经过对象本身,所以并不是所有的虚拟机实现都必须在对象数据上保留类型指针)
如果对象是一个JAVA数组,那么在对象头中还必须有一块用于记录数组长度的数据,因为普通对象的大小可通过元数据信息来获取,而数组不行。
原文:https://www.cnblogs.com/wbyixx/p/11987978.html