JVM从入门到精通-堆

1.堆的核心概述

1.1堆存储内容

一个进程对应一个JVM

进程:只有一个方法区和堆;

线程:共享方法区和堆;

堆中TLAB每个线程都有一块,这块是私有的

在IDEA中设置堆空间大小:

preview

运行JDK目录下的jvisualvm.exe

查看对空间设置大小

对象也可以在栈上分配;

栈里存储的是对象的引用地址值;

堆里存储的是对象;

类的结构存储在方法区中;

堆空间是垃圾回收时,才会清除对象;

1.2 堆的内存分配

JDK7 堆空间内存

JDK8的堆内存分配

目前设置参数只会影响到:新生代+老年代=堆分配的大小

而元空间是无法影响到的

JDK8之后元空间代替了永久代

设置IDEA打印堆空间信息

堆空间打印

而jdk1.7是永久代,不是元空间Metaspace

2.设置堆空间大小

-Xms设置堆起始内存大小;

-Xmx设置堆最大内存大小;

获得运行时方法区实例

查看进程使用内存

注意:

-Xms 600m  -Xmx 600m

但真正能创建对象时,新生代只能是S0或S1中的一个;

所以最后堆内存为575m

同理:但真正能创建对象时,新生代只能是S0或S1中的一个;

3.OOM内存溢出

运行JDK目录下的jvisualvm.exe

可以看出OldMemory无法GC了,因为都满了

运行过程中监控:

4.年轻代与老年代

from和to区是来回切换的,当Survivor0满了,然后清空了就编程了to区;

4.1年轻代与老年代设置

查看设置情况:默认是1:2

EC:150M

S0C:25M

S1C:25M

而现实是自适应内存分配机制是1:1:6;若想1:1:8则需要显式指定

survivor超过一定时间后会移动到老年代

5.图解对象分配过程

总共有8个步骤;

红色的是垃圾了;age年龄计数器;

YGC后,那个Survival是空的就变成了to区;

那S0/S1中的age为15后,就晋升到老年代;

YGC触发条件是Eden区满了时(这时Survivor会一起被GC回收),但Survivor区满了时不会触发YGC;

6.对象分配的特殊情况

 

YGC之后:Eden区会把对象清空,或者把对象移动到Survivor区域;Eden是空的;

若这时年轻代放不下对象,这直接放入到老年代;

若老年代放不下,则老年代触发FullGC;

YGC是Eden空间不够时;survivor区域会被动被处理;这时会把survivor区域需要的保留的对象,移动到老年代;

7.代码举例与JVisualVM演示对象的分配过程

例子:

 

从图中看到,当Eden达到峰值时,触发YGC;这时把Eden的对象搬到Survivor中;Survivor满了后,搬到Old Gen中;

Metaspace元空间没什么变化;

8.JVM调优工具

JProfiler安装JProfiler v11.0.2 

然后在IDEA中下载JProfiler插件;然后在IDEA中启动;

 

 

2.Minor GC、Major GC与Full GC

调优就是减少GC,应用程序的STW暂停,用户进程的干预;

Major GC与Full GC是Minor GC的10倍以上;所以重点调优这两个GC;

2.1三种GC方式

Major GC:只是老年代的垃圾收集

Full GC:收集三个区域;整个Java堆(年轻代,老年代)和方法区的垃圾收集

2.2最简单的分代式GC策略的触发条件

YGC触发条件是Eden满时;随便清理Survivor;而Survivor满了是不会触发YGC的;

YGC会引发STW,暂停其他用户线程;因为要标记哪些是垃圾;

 

threshold默认为15;

老年代GC

一般出现Major GC会伴随至少一次的Minor GC;但非绝对,在Parallel Scavenger收集器的收集策略里就直接进入Major GC

Full GC

Full GC是会触发三个区域;年轻代,老年代和方法区;

这时STW的时间会很长,可能是Minor GC的10倍。

 

例子:

设置参数

当出现OOM时,一般都会出现Full GC;因为只有Full GC后,老年代还不够,则OOM;

2.3堆空间分代思想

年轻代、老年代、永久代

年轻代、老年代、元空间

虚拟机的规范中方法区堆中的一个逻辑部分,但是它却拥有一个叫做非堆(Non-Heap)的别名;

对于方法区的实现,不同虚拟机中策略也不同。HotSpot用永久代/元空间来实现的。

堆:年轻代(Eden、S0、S1)、老年代
元空间(MetaSpace):方法区的实现,堆的逻辑部分
1.存放常量池、方法元信息、Class类元信息
 2.使用本地内存,非堆内存,但逻辑属于堆
 是HotSpot虚拟机方法区的实现

2.4总结内存分配策略(对象提升Promote规则)

默认age15岁时会晋升到老年代;

注意:创建大对象,但却只使用一次;

例子:

NewRatio:老年代与新生代的比例;默认2:1

-Xmx60m 堆内存最大60m;新生代20m,老年代40m;

SurvivorRatio:新生代比例;默认是8:1:1;Eden 8 : S0 1 : S1 1

 

 

3.为堆分配TLAB

堆空间一定是线程共享的吗?

TLAB是线程私有的;不能共享;

为每个线程分配线程缓冲区;

 

默认是开启的 -XX:UseTLAB=true;

 

 

4.堆空间参数设置

若Eden设置的大,会造成minor GC 失去意义;

若Eden设置过小,则会频繁的YGC;

说明

HadnlePromotionFailure空间担保,在1.7之后就永远都是true了

 

5.堆是分配对象的唯一选择吗

6.逃逸分析

若何判断是否逃逸,就是看对象是否在外部被引用。

若对象声明为静态,只要外部使用,仍然会发生逃逸;

只要未发生逃逸,则对象就可以分配到栈上;

 

6.1 判断是否逃逸分析

一个对象的作用域只在本方法内:则未发生逃逸

返回String对象,在常量区,这样就不会逃逸了。

1.7以后默认开启逃逸分析;

查看本进程的JVM参数:

jinfo -flag xxxxx 进程PID 

结论:开发中能使用局部变量的,就不要使用在方法外定义。这样可以方法栈空间了;

6.2代码优化

6.3栈上分配

例子:

-DoEscapeAnalysis 未开启逃逸分析;所有的对象在堆中进行分配;

+DoEscapeAnalysis 开启逃逸分析;

对象未到100万个;

所以逃逸分析好处:

1.分配对象在栈上,不会进行GC;

2.内存中不会维护过多的对象;

3.执行时间比较短;

6.4同步省略

 

编译的时候还会有同步代码块;但运行时会消除掉同步代码

6.5标量替换

 

逃逸分析是在服务器端才有的功能

 

对象还是分配在堆空间上

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值