JVM 深入浅出 - [JVM结构]

一、JVM 结构简图

  • 线程私有:虚拟机栈、程序计数器、本地方法栈
  • 线程共享:堆、方法区, 堆外内存(Java7的永久代或JDK8的元空间、代码缓存)

二、运行时数据区域

1.堆

堆(Heap)在虚拟机启动时建立,它是Java虚拟机管理的内存中最大的一块,用来存放几乎所有java对象的实例,被所有线程共享。

                          默认 新生代(Eden : S1 : S2 = 8 : 1 : 1): 老年代 = 1 : 3

1.1 新生代(Young)

新生代对象朝生夕死(一次垃圾收集一般可以回收70% ~ 95% 的空间),存活率很低,一般都存放再新生代(新生大对象存放在老年代)。新生代又可细分为Eden空间、From Survivor(S1)空间、To Survivor(S2)空间,默认比例为8:1:1。

1.2 老年代(Tenured/Old)

在新生代中经历了多次(虚拟机默认阀值15)GC后仍然可以存活下来的对象会进入老年代中。老年代中的对象一般生命周期都较长,存活率比较高(短命大对象),在老年代中进行GC的频率相对而言较低,而且回收的速度也比较慢。

1.3 永久代(Perm)

永久代存储类信息、常量、静态变量、即时编译器编译后的代码等数据,对这一区域而言,Java虚拟机规范指出可以不进行垃圾收集(一般也不会进行垃圾回收),永久代不属于堆空间,它在JDK 1.8之后被元空间取代。

2.方法区

方法区(Method Area/Non-Heap)用来存放由类加载子系统从文件系统或者网络中加载class信息;除了存放类信息外方法区中还存放常量池(Constant Pool)运行时常量池(Runtime Constant Pool),被所有线程共享。

Java7前,方法区中存储类信息、常量、静态变量、jit编译时代码。
Java7前,常量池是存放在方法区(永久代)中的。
Java7后,常量池是存放到了堆中。 
Java8后,元空间取代了永久代。 运行时常量池和静态常量池存放在元空间中,而字符串常量池依然存放在堆中。

3.虚拟机栈

每个线程在被创建的时都会创建一个虚拟机栈(Java Virtual Machine Stack),其内部由一个个的栈帧(Stack Frame)组成,因此虚拟机栈是线程私有的(无需考虑垃圾回收,与线程同生共死);每运行一个方法就创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法返回值等。每一个方法开始运行到运行完成的过程就是一次入栈和出栈的过程。

栈分为固定大小的栈与可扩容的栈,固定大小的栈在空间不足时会报StackOverflowError,可扩容的栈再空间不足同时无法扩容时会报OutOfMemoryError

堆、栈、方法区的关系

指针定位

句柄定位

地址定位:速度快(减少定位次数),对象地址变更后需要调整Reference。【Hotspot使用】
句柄定位:对象地址信息变动只需调整句柄池中的对象地址,Reference无需调整。
GC标记整理,复制算法都会导致对象内存地址变更。

 4.程序计数器

程序计数器(Program Counter Register)用于保存当前线程执行的内存地址,可以看作当前线程执行的字节码的行号指示器。由于JVM程序是多线程执行的,线程间需要轮流切换,因此为了保证线程切换回来后,还能恢复到原先状态,就需要一个独立的计数器,记录之前中断的地方;程序计数器也是线程私有的(无需考虑垃圾回收,与线程同生共死)。当线程正在执行的是 Java 方法时,程序计数器记录的是 JVM 字节码指令地址,当执行 native 方法时,程序计数器记录的是 undefined。

5.本地方法栈

本地方法栈(Native Method Stack)与虚拟机栈的作用差不多,是为JVM使用到的native方法服务的(本地方法是使用C语言实现的)。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值