JVM之堆和GC垃圾回收

堆的基本内容:

Java堆是虚拟机所管理的内存中最大的一块,Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建,此内存区域的唯一目的就是存放对象实例,Java 世界里“几乎”所有的对象实例都在这里分配内存。

Java堆可以处于物理上不连续的内存空间中,但在逻辑上它应该被视为连续的,这点就像我们用磁盘空间去存储文件一样,并不要求每个文件都连续存放。但对于大对象(例如:数组对象),多数虚拟机实现出于实现简单、存储高效的考虑,很可能会要求连续的内存空间。

注:永久区[元空间]在逻辑上是存在的,而在物理上是不存在的

堆:

一个JVM只有一个堆内存,堆内存的大小是可以进行调节的

堆中的区域详解:

堆内存中还要细分三个区域:

新生区[伊甸园区] Young/New
养老区 old
永久区 Perm

在这里插入图片描述在这里插入图片描述

堆中的对象即使函数执行完毕也不会被删除,那么随着我们不断的在堆中创建对象,如果对象占满了堆,就会导致整个程序挂掉,而Java所提供的GC机制可以帮助我们解决这个问题,首先我们需要判断对象是否需要被回收。通常情况下被栈,本地方法栈,方法区引用的对象不能被删除掉。对于其他可被清理的对象我们可采用下述算法:

标记清理算法(Young区采用的):清理带标记的对象,这是我们最容易想到的方法,就是将堆中可以删除的对象我们给它做一个标记,但这种方法内存中会产生许多内存碎片,所谓内存碎片就是假设在堆中地址不连续的区域有2个1K的对象,如果这两个对象被清理掉,会腾出2个1K的空间,此时我new出来一个2K的对象,它是不能放在任何空间只有1K的地方,并且对象也是不能被拆分的,因此这两个1K的空间就不能被合理利用起来了

标记整理算法(Young区采用的):它是在标记清理算法基础上进行了优化,标记清理算法产生了许多空间不连续的内存碎片,而标记整理算法的实现就是在某个对象被清理之后,使它后面的对象向前移动补充它这个位置,这样虽然能够解决产生内存碎片的缺点,但是它的代价很大,假设当前内存中产生了多个内存碎片,那么使用该方法,必然引起大量对象的移动。

复制算法(Old区采用的):它将整个内存一分为二,分别表示一区和二区,假设我们在一区创建这些对象并且打上对应的标记,当一区内存快满的时候,我们并不是将一区中标记需要删除的对象直接删除,而是将不需要删除的对象复制至二区,并且是紧凑的复制,这样不仅避免了内存碎片问题,而且复制对象不需要大量对象的移动因此开销也不大,但是它最大的缺点就是需要两倍的内存

Young/New Generation 新生代:

程序中新建的对象都将分配到新生代中,新生代又由Eden(伊甸园)与两块Survivor(幸存者) Space 构成

Eden 与Survivor Space 的空间大小比例默认为8:1,这个比例可以通过**-XX:SurvivorRatio 参数来修改**,Young/New Generation的大小则可以通过-Xmn参数来指定

Eden伊甸园:

刚刚新建的对象将会被放置到Eden 中,这个名称寓意着“对象们可以在其中快乐自由的生活”

Survivor Space:

幸存者区域是新生代与老年代的缓冲区域,两块幸存者区域分别为S0 与S1,当年轻代空间不足时,就会触发Minor GC,这里的年轻代空间不足指的是Eden区满,Survivor区满不会触发GC[每次Minor GC 会清理年轻代的内存],当触发Minor GC 后将仍然存活的对象移动到S0中去,随之Eden 就被清空,可以分配给新的对象

当再一次触发Minor GC后,S0和Eden 中存活的对象被移动到S1中,S0即被清空。在同一时刻, 只有Eden和一个Survivor Space同时被操作所以S0与S1两块Survivor 区同时会至少有一个为空闲的。

当每次对象从Eden 复制到Survivor Space 或者从Survivor Space
之间复制,计数器会自动增加其值。,默认情况下如果复制发生超过16次,JVM就会停止复制并把他们移到老年代中去,如果一个对象不能在Eden中被创建,它会直接被创建在老年代中。

新生代GC(Minor GC):

指发生在新生代的垃圾收集动作,因为 Java 对象大多都具备朝生夕灭的特性,通常很多的对象都活不过一次GC,所以Minor GC 非常频繁,一般回收速度也比较快。

Java堆是垃圾收集器管理的内存区域,因此,它也被称作“GC堆”,GC垃圾回收,主要是在伊甸园和养老区,假设内存满了,OOM,堆内存不够,就会出现堆溢出现象。

举例:

import java.util.Random;

public class person{
  public static void main(String[]args){
    String str="hello";
    while(true){
      str+=str+new Random().nextInt(888888888)+new Random().nextInt(999999999);
    }
  }

报错内容如下:

Exception in thread "main" java.lang.OutOfMemoryError: Overflow: String length out of range
	at java.base/java.lang.StringConcatHelper.checkOverflow(StringConcatHelper.java:57)
	at java.base/java.lang.StringConcatHelper.mix(StringConcatHelper.java:138)
	at xxx.person.main(person.java:9)

Java堆既可以被实现成固定大小的,也可以是可扩展的,不过当前主流的Java虚拟机都是按照可扩展来实现的可通过通过参数-Xmx和-Xms设定)。如果在Java堆中没有内存完成实例分配,并且堆也无法再扩展时,Java虚拟机将会抛出OutOfMemoryError异常。

改变当前JVM的堆内存大小的方式:

以idea为例:

在这里插入图片描述

在这里插入图片描述

注:自JDK8以后,永久存储区被称为元空间

永久区:这个区域常驻内存,用来存放JDK自身携带的Class对象。Interface原数据,存储的是java运行时的一些环境或者类信息,这个区域不存在垃圾回收,关闭JVM虚拟就会释放这个区域的内存。

一个启动类,加载了大量的第三方jar包,Tomcat部署了太多的应用,大量生成的反射类,不断地被加载,直到内存满,就会出现OOM

JDK1.6之前:永久代,常量池是在方法区

JDK1.7:永久代,但是慢慢的退化了,去永久代,常量池在堆中

JDK1.8之后:无永久代,常量池在元空间

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

从未止步..

谢谢你的打赏,我会继续努力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值