关于JVM内存区域、内存溢出异常与垃圾回收策略的一点理解

本文详细介绍了JVM内存的各个组成部分,包括程序计数器、虚拟机栈、本地方法栈、Java堆、方法区等,解释了每部分的功能及可能出现的异常情况,并探讨了几种垃圾收集算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JVM内存区域

程序计数器(Program Counter Register)

程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。每个线程都需要一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,即:线程私有。

虚拟机栈(Virtual Machine Stacks)

java虚拟机栈也是线程私有的,他的生命周期与线程相同。虚拟机栈描述的是java方法执行的内存模型,每个方法在执行的同时,会创建一个栈桢,用于存储局部变量、操作数栈、动态链接和方法出入口等信息。即:虚拟机栈是针对于java方法执行过程中所涉及到的内存区域。

本地方法栈(Native Method Stack)

本地方法栈与虚拟机栈所发挥的作用相似,区别在于,虚拟机栈为虚拟机执行JAVA方法服务,而本地方法栈则为虚拟机使用到的Native方法服务。

JAVA堆(Java Heap)

Java堆是虚拟机所管理的内存中最大的一块,Java堆是被所有线程共享的一块内存区域,在虚拟机启动的时候创建,此内存区域的唯一目的是存放对象实例。即:所有的对象实例以及数组,都要在堆上分配内存。Java堆按照不同的角度还可以分为:新生代、老年代或者 Eden空间区域、From Survivor空间、To Survivor空间。

方法区(Method Area)

方法区是各个线程共享的内存区域,用于存储已经被虚拟机加载的类信息、常量、静态变量和即时编译器编译后的代码等数据。

运行时常量池(RunTime Constant Pool)

方法区的一部分,编译器生成的各种字面量和符号引用,在类加载后进入方法区的运行时常量池中。

直接内存(Direct Memory)

它并不是虚拟机运行时数据区的一部分,但这部分内存会被频繁的使用,也可能会导致OutOfMemoryError异常。在进行IO操作时,可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作,避免在Java堆和Native堆中来回复制数据。


内存溢出异常

  1. Java堆溢出。异常代码形如:OutOfMemoryError:Java heap space。
    不停的创建对象,而GC Roots 到这些对象之间有可达路径,那么在对象数量到达最大堆的容量限制后就会产生内存溢出异常。
    可能原因:如果是内存泄漏,泄露的对象与GC Roots相关联,导致垃圾收集器无法回收,通过工具查看泄露对象到GC Roots 的引用链,掌握泄露对象信息和引用链信息,就能够找到引发内存泄露的代码位置。如果不是内存泄露,那说明对象依旧存活,因为无法回收,积累到一定量之后会引发内存溢出,可以通过调整虚拟机的堆参数(-Xmx和-Xms),或者从代码的角度检查是否有对象生命周期过长,持有状态时间过长等问题。

  2. 虚拟机栈和本地方法栈溢出
    如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverFlowError异常。
    如果虚拟机在扩展栈深度是无法申请到足够的内存空间,则抛出OutOfMemoryError异常。

  3. 方法区和运行时常量池溢出,异常代码形如:OutOfMemoryError:PermGen Space
    方法区用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述和方法描述等。所以在使用反射、动态代理和CGlib等技术,运行时生成大量的动态类时,可能会出现内存溢出现象。

  4. 本机直接内存溢出
    DirectMemory容量可以通过 -XX:MaxDirectMemorySize指定,如果不指定,则默认与Java堆的最大值一样,因为直接内存并不是虚拟机的内存区域,所以当虚拟机内存区域+直接内存所使用的区域大于本机内存时,无法申请到本机内存,则抛出异常,除OutOfMemoryError字样外,无其余明显报错信息。


垃圾收集算法

标记-清除 算法

该算法分为“标记”和“清除”两个阶段,首先,标记所有需要回收的对象,在标记完成后,同意回收所有被标记的对象。
缺点
1. 标记和清除两个过程效率都不高。
2. 标记清除后会产生大量不连续的内存碎片。

复制算法

将可用的内存按照容量划分为大小相等的两块,每次只使用一块,再回收的时候,将还存货的对象,复制到另外一块上面,然后,再把已使用的内存空间一次性清理掉。简单,高效。
缺点:可用内存区域变为原来的一半。对象存活率较高时,会进行较多的复制操作,效率降低(在Eden区域中较为合适,不宜在老年代中使用该算法)。
优化:因为大部分的对象都是“朝生夕死”,所以,并不需要按照1:1来划分内存区域,按照8:1来划分区域比较合理,即:Eden区域占80%,From Survivor区域占10%,To Survivor区域占10%。

标记-整理算法

标记整理算法的过程与标记清除算法相似,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象直接都想一端移动,然后直接清理掉端边界意外的内存。

分代收集算法

分代收集算法并不是一种新的算法,可以理解为一种思想,即:根据对象存活周期的不同,划分为几块(新生代和老年代)。新生代中每次垃圾收集,都有大批的对象死去,只有少量存活,适宜选用复制算法,而老年代中因为对象的存活率较高,所以使用标记-清除或者标记-整理算法来进行回收。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值