jvm-------

在这里插入图片描述

1.栈先进后出

1.8同1.7相比,最大的差别就是元空间取代了永久代。元空间的本质和永久代类似,都是堆JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不存在虚拟机中,而是使用本地内存。移除永久代和增加元空间的改变:在永久代时期经常由于永久代内存经常不够用或发生内存泄露,爆出异常java.lang.OutOfMemoryError: PermGen

二、运行时数据区

2.1、程序计数器

程序计数器是一块较小的内存空间,它可以看做是当前线程所执行的字节码的行号指示器。

字节码解释器的工作就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
每条线程都有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储。

2.2 虚拟机栈

虚拟机栈描述的是方法执行的线程内存模型,方法执行的时候,Java虚拟机都会在虚拟机栈里面创建一个栈帧,栈帧用于存储局部变量表操作数栈动态链接方法出口等信息。

每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机中从入栈到出栈的过程。

3. Method Area 方法区

  • 方法区是被所有线程共享,该区域保存所有字段和方法字节码,以及一些特殊方法如构造函数,接口代码也在此定义。静态变量存在方法区中,实例变量存在堆内存中。
  • 运行时常量池 在方法区中有一个非常重要的部分,它是每一个类或接口的常量池表的运行时表示形式

4. 堆

一个Eden区和两个Survivor区,它们之间的比例为(8:1:1)

新生代gc流程:

  • 刚刚新建的对象在Eden中,经历一次Minor GC,Eden中存活对象就会被移动到s0,将这些对象年龄+1,Eden被清空。
  • 等Eden区再满了,就出发一次Minor GC,Eden和s0中存活的对象又会被复制到s1中(这个过程非常重要,因为这种复制算法保证了s1中来自s0和Eden两部分的存活对象占用了连续的空间,避免碎片化)
  • s0和Eden被清空,然后下一轮s0与s1互换角色,如此循环。当对象年龄达到某个年龄(默认值为15)时,就会把它们移到老年代中。

老年代回收算法 标记回收算法、标记整理算法

5.Native Method Stack 本地方法栈

三、本地内存

3.1、元数据区

元数据区也叫元空间,是方法区的一种实现。

存储的是类的元数据信息。

垃圾回收

(1)对新生代的对象的收集称为minor GC;
(2)对旧生代的对象的收集称为Full GC;
(3)程序中主动调用System.gc()强制执行的GC为Full GC。

不同的对象引用类型, GC会采用不同的方法进行回收,JVM对象的引用分为了四种类型:
(1)强引用:默认情况下,对象采用的均为强引用(这个对象的实例没有其他对象引用,GC时才会被回收)
(2)软引用:软引用是Java中提供的一种比较适合于缓存场景的应用(只有在内存不够用的情况下才会被GC)
(3)弱引用:在GC时一定会被GC回收
(4)虚引用:由于虚引用只是用来得知对象是否被GC
 

Full GC触发机制
触发Full GC执行的情况有如下五种:

调用System.gc(),系统建议执行Full GC,但是不必然执行
老年代空间不足
方法区空间不足
通过Minor GC后进入老年代的平均大小大于老年代的可用内存
由Eden区,from区向to区复制时,对象大小大于to区可用内存,则把对象转存到老年代,并且老年代的可用内存小于该对象大小(那要是GC之后还不够呢?那还用说:OOM异常送上)
 

为什么会产生StackOverflowError?

答:因为一个线程把Stack内存全部耗尽了,一般是递归函数造成的。

13.问:JVM中到底哪些区域是共享的?哪些是私有的?

答:Heap和Method Area是共享的,其他都是私有的,

什么时候进入STW状态?

minor gc,full gc,会发生。可达性分析算法中枚举根节点(GC Roots)会导致所有Java执行线程停顿,进入STW状态

虚拟机并不是永远地要求对象年龄必须达到了-XX:MaxTenuringThreshold=15才能晋升老年代;

动态年龄判断: Survivor区的对象年龄从小到大进行累加,当累加到X年龄(某个年龄)时占用空间的总和大于50%(可以使用-XX:TargetSurvivorRatio=?来设置保留多少空闲空间,默认值是50),那么比X年龄大的对象都会晋升到老年代;
 

java是根据什么来执行可达性分析的:

根据GC ROOTS。GC ROOTS可以的对象有:虚拟机栈中的引用对象,方法区的类变量的引用,方法区中的常量引用,本地方法栈中的对象引用。

3.-gc 垃圾回收统计

[root@hadoop ~]# jstat -gc 3346 #用于查看JVM中堆的垃圾收集情况的统计
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU YGC     YGCT    FGC    FGCT     GCT  
128.0  128.0   0.0   128.0   1024.0   919.8    15104.0     2042.4   8448.0 8130.4 1024.0 996.0       7    0.019   0      0.000    0.019


S0C:年轻代中第一个survivor(幸存区)的容量 (字节)
S1C:年轻代中第二个survivor(幸存区)的容量 (字节)
S0U:年轻代中第一个survivor(幸存区)目前已使用空间 (字节)
S1U:年轻代中第二个survivor(幸存区)目前已使用空间 (字节)
EC:年轻代中Eden(伊甸园)的容量 (字节)
EU:年轻代中Eden(伊甸园)目前已使用空间 (字节)
OC:Old代的容量 (字节)
OU:Old代目前已使用空间 (字节)
MC:metaspace(元空间)的容量 (字节)
MU:metaspace(元空间)目前已使用空间 (字节)
CCSC:当前压缩类空间的容量 (字节)
CCSU:当前压缩类空间目前已使用空间 (字节)
YGC:从应用程序启动到采样时年轻代中gc次数
YGCT:从应用程序启动到采样时年轻代中gc所用时间(s)
FGC:从应用程序启动到采样时old代(全gc)gc次数
FGCT:从应用程序启动到采样时old代(全gc)gc所用时间(s)
GCT:从应用程序启动到采样时gc用的总时间(s)

 

jps全称为Java Virtual Machine Process Status Tool,是Java提供的一个查看当前用户有权访问的主机的Java进程情况的工具。jps命令在jdk的JAVA_HOME/bin/目录下面。

jstack -l pid > log.log 输出线程堆栈信息

万猫学社

要获得某个对象的大小,可以将其总大小除以该对象类型的数量。如果指定了live参数,则只计算活动的对象。比如:

万猫学社.png

 

CMS垃圾回收器:

CMS是作用于老年代的并发垃圾回收器,使用标记清除算法,工作流程是:初始标记,并发标记,再次标记,并发清除

优点

耗时最长的并发标记和并发清除阶段gc线程和用户线程是并发执行的,因此其STW时间短,适合对延迟有要求的任务
缺点:

  • CMS在老年代使用的是标记清除算法,会产生大量内存碎片
  • GC线程与用户线程并发执行,二者会抢占cpu,并且会产生浮动垃圾
  • 初始标记阶段会发生短暂的stw,用于标记GCRoot对象能够直接到达的对象
  • 并发标记阶段gc线程根据GCRoot对象标记可到到的存活对象,应用程序可以和gc线程并行进行,不需要stw
  • 再次标记阶段会进行stw,目的是为了修正因为并发标记阶段应用程序和gc线程并发执行产生的浮动垃圾
  • 并发清除阶段gc线程清除垃圾对象,gc线程和应用线程并发执行因此会产生浮动垃圾,在下一次gc清理该浮动垃圾

G1垃圾回收器:

G1垃圾回收器是一款可以同时管理新生代和老年代,在老年代使用标记整理算法,其最大的特点是将内存划分为多个大小相等region,每个region都可以作为伊甸区,survivor区,老年代

优点:

  • 老年代使用标记整理算法,不会产生内存碎片
  • 使用region,不会出现新生代或者老年代分配空间过大而造成浪费
  • 每次只选择垃圾对象多的region,而不是整个堆,大幅减少了STW时间(但region与region之间是有依赖关系的,g1维护了一个Remembered Set记忆集记录了region的依赖关系,只需要扫描关联的region,而不是整个堆)
  • 用于可预测停顿的模型,可以指定STW时间(也就是可预测停顿),比如在一小时内垃圾回收导致的"stop the world"时间不超过一分钟。

G1垃圾回收过程主要包含三个阶段,

  • 当伊甸区慢时,年轻代使用标记复制算法进行回收
  • 当堆空间的内存占用达到阈值时,老年代使用标记整理算法进行回收,前三个过程和cms类似,都为初始标记,并发标记,再次标记,区别在于最终清除阶段,CMS是并发的,而G1会进行STW,不是并发的
  • 当老年代占比达到阈值,触发混合回收,为了防止堆内存耗尽,会回收所有年轻代和部分老年代

CMS和G1比较:

  • G1和CMS都分为4个阶段,前三个阶段基本相同都为初始标记,并发标记,再次标记,区别在于最后清除阶段CMS是并发的,G1不是并发的,因此CMS最终会产生浮动垃圾,只能等待下次gc才能清除
  • G1可以管理整个堆,而CMS只能作用于老年代,并且CMS在老年代使用的是标记清除算法,会产生内存碎片,而G1使用标记整理算法,不会产生内存碎片
  • G1相比于CMS最大的区别是G1将内存划分为大小相等的Region,可以选择垃圾对象多的Region而不是整个堆从而减少STW,同时使用Region可以更精确控制收集,我们可以手动明确一个垃圾回收的最大时间

优化

为了提高热点代码的执行效率,在运行时, 虚拟机将会通过即时编译器(JIT compiler,just-in-time compiler)把这些代码编译成与 本地平台相关的机器码,并进行各层次的优化,然后存储在内存中,之后每次运行代码时, 直接从内存中获取即可

1. 清楚从程序角度,有哪些原因导致FGC? 

  • 大对象:系统一次性加载了过多数据到内存中(比如SQL查询未做分页),导致大对象进入了老年代。

  • 内存泄漏:频繁创建了大量对象,但是无法被回收(比如IO对象使用完后未调用close方法释放资源),先引发FGC,最后导致OOM.

  • 程序频繁生成一些长生命周期的对象,当这些对象的存活年龄超过分代年龄时便会进入老年代,最后引发FGC. (即本文中的案例)

  • 程序BUG导致动态生成了很多新类,使得 Metaspace 不断被占用,先引发FGC,最后导致OOM.

  • 代码中显式调用了gc方法,包括自己的代码甚至框架中的代码。

  • JVM参数设置问题:包括总内存大小、新生代和老年代的大小、Eden区和S区的大小、元空间大小、垃圾回收算法等等。

3. 通过jmap命令查看堆内存中的对象,并按空间排序

通过命令 jmap -histo 7276 | head -n20

上图中,按照对象所占内存大小排序,显示了存活对象的实例数、所占内存、类名。可以看到排名第一的是:int[],而且所占内存大小远远超过其他存活对象。至此,我们将怀疑目标锁定在了 int[] .

4. 进一步dump堆内存文件进行分析

锁定 int[] 后,我们打算dump堆内存文件,通过可视化工具进一步跟踪对象的来源。考虑堆转储过程中会暂停程序,因此我们先从服务管理平台摘掉了此节点,然后通过以下命令dump堆内存:

jmap -dump:format=b,file=heap 7276

通过JVisualVM工具导入dump出来的堆内存文件,同样可以看到各个对象所占空间,其中int[]占到了50%以上的内存,进一步往下便可以找到 int[] 所属的业务对象,发现它来自于架构团队提供的codis基础组件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值