Java 内存结构概览

当我们启动一个Java程序时,JVM会向操作系统申请一块连续的内存空间,JVM会将申请的内存空间按照功能划分为以下几个部分:

一,程序计数器

程序计数器(Program Counter)是每个线程独有的一小块内存,用来记录当前线程正在执行的字节码指令的位置,可以简单理解为“当前代码行的编号”。

因为Java是多线程的,每个线程在CPU上不是一直运行的,CPU需要在多线程间来回切换(就是线程间上下文切换)。当线程1被CPU切走后,再切回来,就需要知道上次执行到了哪一行代码,这时候就需要程序计数器了,确保线程可以恢复到正确的位置。

也因为此,程序计数器是线程私有的,每个线程独自维护一份。

程序计数器也是JVM中唯一不会发生OOM的内存区域,因为它非常小,结构也简单,只是记录一个位置。

二,元数据区

元数据区在1.8以前叫永久代,是JVM内部堆的一部分,但是有几个问题,容量固定,类卸载复杂,和堆共享GC配置,调优麻烦,所以从1.8开始,永久代被废除。

Metaspace:使用是本地内存,不再占用JVM的堆空间,默认情况下只受限于物理内存,但是也可以显示地设置它。

Metaspace用来存储类的结构信息(类名,方法,字段,继承结构等),常量池,类加载器等,其实Metaspace正常情况下都是安静的,它不像堆那样有对象不断创建和回收,一般情况下,程序启动时加载的类结构是固定的,对 Metaspace 的占用也基本确定。

只有在加载新的class或动态生成新的类(动态代理,字节码框架),才会新增Metaspace中的内容。如果加载或生成了大量类,导致元数据信息越积越多,也会导致元数据区撑爆。

触发的异常一般是:

java.lang.OutOfMemoryError: Metaspace

三,本地方法区

它是用于Java调用native方法(C/C++编写的)时使用,通常配合JNI技术。

对于大部分Java开发者来说不需要特别关注,在 HotSpot 虚拟机中,本地方法栈的实现与 JVM 栈类似,底层结构一致,通常合并管理。

四,栈

栈是线程私有的,每个线程启动都会分配一个独立的JVM栈,它的生命周期是跟随线程的,线程结束后栈也会销毁。

栈的核心结构是栈帧,每当线程执行一个方法,就会创建一个新的栈帧,压入栈顶,每个栈帧包含:

  • 局部变量表(保存基本类型,对象引用等)
  • 操作数栈(用于执行指令时的临时结果保存,比如执行 int sum = a + b 时,a、b 会被压入操作数栈中,计算结果再弹出。)
  • 方法返回地址(记录当前方法执行完后应该返回的位置)
  • 动态链接,指向常量池中方法或字段的引用,比如System.out.println,需要去运行时常量池查找System.out.println对应的真实地址去执行。

栈空间太小时,可能会出现以下两种异常

  • StackOverflowError:方法递归太深,栈帧无限增长。
  • OutOfMemoryError:线程数过多,每个线程都要分配栈。

栈内存不会参与GC,因为方法退出后,栈帧会自动销毁。

五,堆

堆是JVM中内存最大的一块,所有线程共享,也是GC主要管理的区域。

所有创建的对象实例和数组都会分配在堆内存。

为了提高垃圾回收效率,堆会进一步被划分为:

新生代:大部分新创建的对象都会在这里,生命周期短,回收频率高。

        新生代也会进一步划分为:Eden 区、Survivor0 区 和 Survivor1 区,默认比例为 8:1:1

老年代:经过多次GC仍存活的对象被移入到这里,生命周期长,GC代码高。

JVM的主要GC算法就是围绕这两个区域来展开的。

一个对象的生命周期

当我们new一个对象时,

  • 优先尝试栈上分配,JVM会做逃逸分析,判断这个对象是否逃逸出了当前方法作用域,如果对象生命周期明确,不会逃逸,JVM可能直接分配在栈帧里,随着方法结束被销毁,无需GC。
  • 如果不能栈上分配,则会被分配到新生代的Eden区

接下来,如果发生一次Minor GC:

  • Eden 中仍存活的对象会被复制到 Survivor0 区
  • 之后如果再发生一次GC,将 Survivor0 区仍存活的对象复制到Survivor1 区(死亡对象直接清除)
  • 并在这两个Survivor 区之间来回“拷贝”(复制算法),每次年龄 +1

当对象的年龄增长到一定值(默认是15岁,可配置),就会晋升到老年代。除了正常的熬资历,还有一种提前晋升的情况:

如果Survivor区无法容纳所有需要复制的对象(存活对象太多),JVM会将部分年龄较大的对象直接晋升到老年代。

动态年龄判断

关于直接晋升的机制称为“动态年龄判断”,具体逻辑:

  • JVM会遍历所有即将复制的对象,按年龄累加其内存大小
  • 当Survivor区要拷贝的所有对象中,某个年龄及以上的对象所占总内存大小 > Survivor区容量的一半,这个年龄及以上的对象会一起晋升到老年代。

对象的晋升过程既有年龄判断,也考虑到了Survivor区的空间压力。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值