Java基础-JVM内存管理-堆内存

Java工程师知识树 / Java基础


概要

存在一个堆内存,堆也是 java 内存管理的核心区域。Java 堆区在 JVM 启动的时候即被创建,其空间大小也就确定了。是 JVM 管理的最大的一块内存空间。

《Java虚拟机规范》规定,堆可以处于物理上不连续的内存空间中,但在逻辑上它应该被视为连续的。

所有的线程共享 Java 堆,在这里还可以划分线程私有的缓冲区(Thread Local Allocation Buffer, TLAB)

《Java虚拟机规范》中对 Java 堆的描述是:所有的对象实例以及数组都应当在运行时分配在堆上。( The heap is the run-time data area from which memory for all class instances and arrays is allocated)

数组和对象可能永远不会存储在栈上,因为栈帧中保存引用,这个引用指向对象在堆中的位置。

在方法结束后,堆中的对象不会马上被移除,仅仅在垃圾收集的时候才会被移除。

堆,是 GC (Garbage Collection,垃圾收集器) 执行垃圾回收的重点区域。

一句话描述:Java堆是JVM内存中最大的一块区域,JVM几乎所有的对象实例都在堆里分配内存并创建。堆内部区域的划分取决于JVM的垃圾回收策略,即GC策略。目前主流的GC策略大部分是基于分代收集算法的,如 parNew+CMS,或者G1等等。因此我们可以将Java堆再划分为新生代老年代

存储数据

堆中存储:1.运行时类实例(即对象,包含全局变量);2.运行时数组;3.静态变量;4.运行时常量池。

不能在栈上存储数组和对象,因为栈帧被设计为创建以后无法调整大小。

栈帧只存储指向堆中对象或数组的引用。与局部变量数组(每个栈帧:调用方法创建栈帧)中的原始类型和引用类型不同,对象总是存储在堆上以便在方法结束时不会被移除。

对象和数组永远不会显式回收,而是由垃圾回收器自动回收。

通常,过程是这样的:

  1. 新的对象和数组被创建并放入新生代。
  2. Minor垃圾回收将发生在新生代。依旧存活的对象将从 eden 区移到 survivor 区。
  3. Major垃圾回收一般会导致应用进程暂停,它将在三个区内移动对象。仍然存活的对象将被从新生代移动到老年代。
  4. 每次进行老年代回收时也会进行永久代回收。它们之中任何一个变满时,都会进行回收。

堆的限制

OutOfMemoryError产生原因主要包含下面几种情况:

  • 内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
  • 集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
  • 代码中存在死循环或循环产生过多重复的对象实体;
  • 使用的第三方软件中的BUG;
  • 启动参数内存值设定的过小;

堆空间的大小设置:

-Xms 用于表示堆区的起始内存,等价于 -XX:InitialHeapSize ,eg:-Xms256M
-Xmx 用于表示堆区的最大内存,等价于-XX:MaxHeapSize ,eg:-Xmx256M

一旦堆区中的内存大小超过 “-Xmx" 所指定的最大内存是,将会抛出 OutOfMemoryError异常。
通常会将 -Xms-Xmx 两个参数配置相同的值,其目的是为了能够在 java 垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小,从而提高性能。

默认情况下,初始内存大小:物理电脑内存大小 / 64最大内存大小:物理电脑内存大小 / 4

堆内存区域

堆被分为了下面三个区域:

新生代:1.Eden区(伊甸区);2.Survivor区(幸存者区)0.1
老年代:养老区
永久代(jdk7以前)/元空间(jdk8以后):1.8开始持久代被废弃,使用元空间代替,元空间MetaSpace并不是堆内存的一部分而是本地内存。

堆区与非堆区的区别

第一,Perm Gen(永久代)--1.8元空间

元空间是jdk8以后才加入的,用来替换原来的永久代。也就是说,原perm区(永久代)中的方法区,也在这里。从它原来的名字就可以看出来,永久代指的就是那些变动很少的数据,稳定为主。比如我们在jvm启动时,加载的那些class文件;以及在运行时,动态生成的代理类。

元空间的大小,默认是没有上限的。极端情况下,会一直挤占操作系统的剩余内存。

第二、CodeCache(代码缓存区)

CodeCahe存放的,就是即时编译器所生成的二进制代码。当然,JNI的代码也是放在这里的。

这个空间在不同的平台,大小都是不一样的,但一般够用了。但是把这个区域调的非常的小的情况下,JVM不会溢出,这个区域也不会溢出,但是会退化成解释型执行模式,速度和JIT不可同日而语,慢个数量级也是可能的。

第三、年轻代与老年代

存储在 JVM 中的 Java 对象可以被划分为两类:

    1. 一类是生命周期较短的瞬时对象,这类对象在创建和消亡都非常迅速
    1. 另外一类对象的生命周期却非常长,在某些极端的情况下还能够与 JVM 的生命周期保持一致。

Java堆区进一步细分的话,可以划分为年轻代(YoungGen) 和老年代(OldGen)。年轻代又称为新生代/新生区。老年代又称为老年区/养老区。

其中年轻代有可以划分为 Eden 空间 、Survivor0 空间和 Survivor1 空间(有时也叫做 from区、 to 区)

年轻代与老年代关系

大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当这个Survivor区满时,此区的存活对象将被复制到另外一个Survivor区,当这个Survivor去也满了的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制“年老区(Tenured)”。

需要注意,Survivor的两个区是对称的,没先后关系,所以同一个区中可能同时存在从Eden复制过来 对象,和从前一个Survivor复制过来的对象,而复制到年老区的只有从第一个Survivor去过来的对象。而且,Survivor区总有一个是空的。同时,根据程序需要,Survivor区是可以配置为多个的(多于两个),这样可以增加对象在年轻代中的存在时间,减少被放到年老代的可能。

配置年轻代与老年代在对结构的占比:

年轻代与老年代的比例默认时 1:2;年轻代区的默认比例时 8:1:1

  • 默认 -XX: NewRaio = 2, 表示新生代占1, 老年代占2, 新生代占整个堆的 1/3
  • 可以修改 -XX: NewRatio = 4, 表示新生代占1, 老年代占4, 新生代占整个堆的1/5。

在 HotSpot 中,Eden 空间和另外两个 SurvIvor 空间缺省所占的比例是 8:1:1 开发人员可以通过选项 -XX:SurvivorRatio 调整整个空间比例。比如: -XX:SurvivorRatio = 8

几乎所有的 Java 对象都是在 Eden 区被new 出来的。绝大部份的 Java 对象的销毁都年轻代在进行了。可以使用选项 -Xmn 设置年轻代最大内存大小。

第四、对象分配与回收过程

  • 针对幸存者s0, s1区的总结:复制之后有交换,谁空谁是to。
  • 关于垃圾回收:频繁在新生区收集,很少在养老区收集,几乎不在永久区/元空间收集。

对象的分配策略

(1)对象优先在Eden空间分配

当我们创建一个对象时,若没有特殊情况,这个对象会被分配在新生代的Eden空间。此时,若Eden空间不足,无法提供这个对象所需的空间时,将会触发垃圾回收机制,清理Eden空间,为新对象腾出更多空间。在进行垃圾回收时,会将Eden中仍然存活的对象放入一个空闲的Survivor中,但是若Survivor空间不足以存放这些存活的对象,则由于担保机制的存在,这些对象会被放入到老年代中。

(2)大对象直接在老年代分配

假设我们在代码中创建了一个很大的对象(比如数组或较长的字符串),而这个对象在很长一段时间都要使用,不会轻易被当作垃圾回收。这时候将面临一个问题:若将这个对象分配在新生代的Eden中,每次进行垃圾回收时,都需要将这个大对象复制到Survivor中保留,这个复制过程是一笔很大的开销,而且由于大对象占用了大量空间,垃圾回收将会频繁发生。所以,为了避免这种情况的发生,对于较大的对象,将会被直接分配到老年代中,原因是老年代发生垃圾回收的频率较低,而且不是使用复制算法进行垃圾回收。

那如何判断一个对象是否属于大对象呢?JVM提供一个参数-XX:PretenureSizeThreshold,通过这个参数来设定多大属于大对象。当需要为一个对象分配空间时,若此对象所需的空间大于这个参数的值,就会被判定为一个大对象,从而在老年代中为其分配空间。

(3)长期存活的对象将进入老年代

这个原则合情合理,老年代存在的首要目的,就是存放生命周期较长的对象,所以对于新生代中存活了很长时间的对象,就应该把他们移入老年代,而不是一直留在新生代中占用空间。每一个对象都有一个自己的年龄计数器,记录了自己的存活周期。对于新生代中的对象,初始时刻它的年龄计数器为0,每经历一次垃圾回收后,年龄计数器+1。当计数器的值到达设定好的阈值时(默认是15),就证明这个对象是一个”老油条“,于是将它转入到老年代中。

对于生命周期长的对象,由于不会轻易死亡,所以每一次垃圾回收都会被它拖慢,而且垃圾回收对于短时间内不会死亡的对象也没有意义,所以不应该将它留在垃圾回收频繁的新生代占用空间,这就是需要将这种对象转让老年代的理由。JVM中也提供了一个参数-XX:MaxTenuringThreshold来设置对象进入老年代的阈值,当对象的年龄计数器超过这个值时将进入老年代。

(4)动态年龄判断

对于新生代中的对象,并不一定需要年龄计数器到达阈值才被放入老年代,有一种特殊情况会导致对象直接进入老年代。当新生代的Survivor空间中,某一个年龄的对象相加,所占空间总和超过了Survivor空间的一半,则大于或等于这个年龄的对象将直接进入老年代,而不需要到达阈值。比如说,在Survivor空间中,年龄为5的对象相加,所占空间超过了Survivor总空间的一半,则所有年龄>=5的对象,会被直接转入老年代。

第五、新生代与老年代使用的GC算法

对于新生代而言,这一块区域中的对象存活时间短,每一次垃圾回收都能回收大部分内存,所以适合使用复制算法进行垃圾回收,同时以老年代作为这个算法的担保空间;

对于老年代而言,每次垃圾回收只能释放小部分空间,若使用复制算法,每次将需要做大量复制,而且此时Survivor需要较大的空间,所以不适合使用复制算法,因此在老年代中,一般使用标记—清除或者标记—整理算法进行垃圾回收。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,776评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,527评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,361评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,430评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,511评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,544评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,561评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,315评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,763评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,070评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,235评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,911评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,554评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,173评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,424评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,106评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,103评论 2 352

推荐阅读更多精彩内容