创建对象在堆区如何分配内存


一 JVM内存模型

1.什么是JVM?

(1)JVM是一种用于计算设备的规范,它是一个虚构出来的机器,是通过在实际的计算机上仿真模拟各种功能实现的。

(2)JVM包含一套字节码指令集,一组寄存器,一个栈,一个垃圾回收堆和一个存储方法域。

(3)JVM屏蔽了与具体操作系统平台相关的信息,使Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。JVM在执行字节码时,实际上最终还是把字节码解释成具体平台上的机器指令执行。

 2.JDK、JRE、JVM是什么关系?

(1)JRE(Java Runtime Environment),也就是java平台。所有的java程序都要在JRE环境下才能运行。

(2)JDK(Java Development Kit),是开发者用来编译、调试程序用的开发包。JDK也是JAVA程序需要在JRE上运行。

(3)JVM(Java Virtual Machine),是JRE的一部分。它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

3.运行时数据区域的划分

JVM虚拟机在执行Java程序的过程中会把它管理的内存划分成若干个不同的数据区域。

JDK1.8之前分为:线程共享(Heap堆区、Method Area方法区)、线程私有(虚拟机栈、本地方法栈、程序计数器)

JDK1.8以后分为:线程共享(Heap堆区、MetaSpace元空间)、线程私有(虚拟机栈、本地方法栈、程序计数器)

程序计数器(Program Counter Register)

字节码解释器在解释执行字节码文件工作时,每当需要执行一条字节码指令时,就通过改变程序计数器的值来完成。程序中的分支、循环、跳转、异常处理、线程恢复等功能都需要依赖这个计数器来完成。

程序执行过程中,会不断的切换当前执行线程,切换后,为了能让当前线程恢复到正确的执行位置,每条线程都需要一个独立的程序计数器,并且各线程之间计数器互不影响,独立存储。

程序计数器的主要作用

字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。

在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候,能够知道当前线程的运行位置。

程序计数器是唯一一个不会出现 OutOfMemoryError 的内存区域,它随着线程的创建而创建,随着线程的结束而死亡。

 二 什么是堆(Heap)

Heap堆区,是用于存放对象实例和数组。

Heap堆是JVM所管理的内存中最大的一块区域,被所有线程共享的一块内存区域。堆区中存放对象实例,“几乎”所有的对象实例以及数组都在这里分配内存。

Java世界中“几乎”所有的对象都在堆中分配,但是,随着 JIT 编译器的发展与逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致 一些微妙的变化,所有的对象都分配到堆上也渐渐变得不那么“绝对”了。从JDK1.7开始已经默认开启逃逸分析,如果某些方法中的对象引用没有被返回或者未被外面使用(未逃逸出去),那么对象可以直接在栈上分配内存。

三 新生代、老年代

Heap 堆是垃圾收集器GC(Garbage Collected)管理的主要区域,因此堆区也被称作GC堆。从垃圾回收的角度,由于现在收集器基本都采用垃圾收集算法,所以JVM中的堆区往往进行分代划分,例如:新生代和老年代。目的是更好地回收内存,或者更快地分配内存。

 

堆区大小 = 新生代(1/3) + 老年代(2/3)

新生代占整个堆区的三分之一,新生代被分为两部分,一部分是Eden区,另一部分是Survivor(幸存者区)区,Eden占整个新生代的百分之八十,而Survivor占比百分之二十。Survivor区又被分为from和to,也被称为S0和S1

老年代占整个堆区的三分之二

 如何在查看堆区中的新生代、老年代的空间分配比例 ?可以通过 java -XX:+PrintFlagsFinal -version 命令查看

InitialSurvivorRatio = 8

新生代Young(Eden/Survivor)空间的初始比例 = 8:代表Eden占新生代空间的80%;

uintx NewRatio = 2

老年代Old / 新生代 Young的空间比例 = 2 : 代表老年代Old是新生代Young的2倍

因为新生代是由 Eden + s0 + s1 组成的,所以按照上述默认比例,如果 Eden 区内存大小是 40M,那么两个 Survivor 区就是 5M,整个新生代区就是 50M,然后可以算出 Old 区内存大小是 100M,堆区总大小就是 150M。

四 创建对象的内存分配

创建一个新对象,在堆中的分配内存。

大部分情况下,对象会在 Eden 区生成,当 Eden 区装填满的时候,会触发 Young Garbage Collection,即 YGC垃圾回收的时候,在 Eden 区实现清除策略,没有被引用的对象则直接回收。

依然存活的对象会被移送到 Survivor 区。Survivor 区分为 s0 和 s1 两块内存区域。每次 YGC的时候,它们将存活的对象复制到未使用的Survivor 空间(s0 或 s1),然后将当前正在使用的空间完全清除,交换两块空间的使用状态。每次交换时,对象的年龄会加+1。

如果 YGC 要移送的对象大于 Survivor 区容量的上限,则直接移交给老年代。一个对象也不可能永远呆在新生代,在 JVM 中 一个对象从新生代晋升到老年代的阈值默认值是 15,可以在 Survivor区交换 14 次之后,晋升至老年代。

分配策略

对象优先在Eden区分配

大对象直接进入老年代

 内存分配过程中的GC

HotSpot JVM 的内存分配过程中,GC垃圾回收分类有两大种:

1.部分收集 (Partial GC):

        新生代收集(Minor GC / Young GC):只对新生代进行垃圾收集;

        老年代收集(Major GC / Old GC):只对老年代进行垃圾收集。需要注意的是 Major GC 在有的时候中也代指整堆收集;

        混合收集(Mixed GC):对整个新生代和部分老年代进行垃圾收集;

2.整堆收集 ( Full GC ):收集整个 Java 堆和方法区;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值