再看 JVM(2)

文章太长了,分2篇写吧,上一篇:再看 JVM(1)java

堆内存

想必你们对堆内存都是耳熟能详了,本身都去仔细研究过了,可是这里仍是要重点说明,尽可能力争全面,有些细节点的点仍是有不少人不知道的 φ(≧ω≦*)♪算法

堆内存特色

  • 几乎全部的对象实例都在堆空间分配内存
  • 方法结束后,堆中的对象不会立刻被移除,仅仅在垃圾回收时才会移除
  • 栈上保存对象引用,对象自己仍是储存在堆内存中的

TLAB 线程私有缓冲区

这个不少人不知道哦 ヽ(✿゚▽゚)ノ数组

由于堆是进程内线程间共享的,那么天然并发争强数据是少不了的,为了减小线程之间因并带来的性能损失,推出了 TLAB 这么个技术并发

TLAB:线程私有缓冲区,堆里面分出一小部分空间来,再分红好几块,每一个线程占一块,那就是每一个线程独有一下块,每个小块称为 TLAB 并发性更好,没有竞争 app

堆内存结构

大部分现代垃圾收集器都是基于分带收集理论设计,而决定堆空间的又是GC,因此 JVM 采用哪一种类型的垃圾收集器,堆空间的结构就趋近哪一种构型设计工具

咱们以 JDK1.8 Hotspot 虚拟机为准,目前开发绝大部分都是 JDK1.8 的,由于以后就收费了 o( ̄ヘ ̄o#)post

对象分2类:性能

  • 生命周期很是端的瞬时对象,这类对象的建立和销毁都很是快,好比方法里面建立的对象
  • 生命周期很是长,某些极端状况下和JVM生命周期同样长,好比okhttp的单例,application的单例,最典型的就是一些连接的操做对象了,妥妥的超长生命周期 o(一︿一+)o

堆内存和GC算法都是围绕这2种生命周期的对象来的,堆内存分红:新生代和老年代2个部分,新生代保存那些生命周期短的对象,尤为是通过IBM的研究,80%的对象生命周期都只有很是短,像栈帧里面开辟的对象都是这种类型的。老年代保存那些生命周期及其漫长的对象,新生代的对象要是通过足够GC次数后会升级到老年代里优化

至于 Eden、S0、S1 懒的说了,你们本身去查把,估计你们都知道 o( ̄ε ̄*)spa

Eden、S0、S1 默认的比例是 8:1:1,但打印出来其实是 6:1:1,原理是默认开启了 自适应内存分配策略,固然你就是把它关了也仍是 6:1:1,除非你经过 -XX:SurvivorRatio=8 显示指定才有效

堆内存 JVM 配置、命令

  • Xms - 堆内存初始值,默认=物理内存的 1/64
  • Xmx - 堆内存最大值,默认=物理内存的 1/4
  • Xms500m - VM options 这么写
  • -XX:NewRatio=2 - 新生代老年代比例,2的意思是新生代是1占总数的1/3,老年代代是2占总数的2/3,通常咱们不改这个参数,由于新生代小了,意味这GC回收频率就要高了
  • -XX:SurvivorRatio=8 - Eden、S0、S1 的比例,8的意思 Eden 是8占总数的8/10,S0是1占总数的1/10,S1是1占总数的1/10
  • Xmn - 新生代最大值,通常不动,通常都用比例,这个写了比例就不算数了
  • -XX:+UseAdaptiveSizePolicy - 自适应内存分配策略,-号是取消设置,+号是采用设置,这个其实不起做用的...
  • jinfo -flag NewRatio 进程ID - 打印新生代老年代比例
  • jinfo -flag SurvivorRatio 进程ID - 打印新生代内比例

经过 Runtime 对象能够获取这2个参数

long Xms = Runtime.getRuntime().totalMemory();
long Xmx = Runtime.getRuntime().maxMemory();
复制代码

通常状况下咱们把 Xms、Xmx 设置成相等的,为的是减小系统压力。他俩要是不等的话,堆内存在需求增加的状况下会不停的去申请内存,新申请的内存和原来内存是不连续的,内存碎片化会下降内存读写性能。内存需求减小的状况下,系统会回收堆内存不用的空间,这样频繁的来来回回申请、回收内存会极大的系统压力,更况且GC自己就很耗费性能还会阻塞用户进程,GC以后咱们再来这么一下系统性能压力就更大了 (๑•̀ㅂ•́)و✧

打印堆内存有2个方式:

  • jsts -gc 进程ID: 这是命令行的,随时都能能用
  • -XX:+PrintGCDetails: 这是配置到 VM options 里面的,只有进程结束时才能代印出数据,前面章节介绍过了

看下命令行打印出来的数据,认识下参数:

➜  ~ jstat -gc 28763
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT
25600.0 25600.0  0.0    0.0   153600.0 61443.3   409600.0     0.0     4480.0 774.4  384.0   75.9       0    0.000   0      0.000    0.000
复制代码
  • C结尾的是总数,U结尾的是使用量
  • EC/EU: 新生代
  • OC/OU: 老年代
  • S0C/S0U: S0
  • S1C/S1U: S1

不过这里有个点要知道,咱们用代码把堆内存打印出来,参数咱们设置的是:-Xms600m -Xmx600m

public static void main(String[] args) {

    long Xms = Runtime.getRuntime().totalMemory() / 1024 / 1024;
    long Xmx = Runtime.getRuntime().maxMemory() / 1024 / 1024;

    System.out.println("Xms:" + Xms);
    System.out.println("Xmx:" + Xmx);

}
复制代码

实际打印出来的是 575,为啥???

Xms:575
Xmx:575
复制代码

由于这里少了一个 s1 的大小,堆内存中真正能存数据的就是 Ednt+s0或者s1其中的一个,s0、s1 是为了相互赋值的,一个时刻只有一个用来存储对象数据,另外一个留着准备给GC复制对象用

jvisualvm 截图看下,S0、S1 的大小是25M

实际上 -XX:+PrintGCDetails 打印出来的新生代的 total 也是575,也是不算 S1 的

Xms:575
Xmx:575
Heap
 PSYoungGen      total 179200K, used 12288K [0x00000007b3800000, 0x00000007c0000000, 0x00000007c0000000)
  eden space 153600K, 8% used [0x00000007b3800000,0x00000007b44001b8,0x00000007bce00000)
  from space 25600K, 0% used [0x00000007be700000,0x00000007be700000,0x00000007c0000000)
  to   space 25600K, 0% used [0x00000007bce00000,0x00000007bce00000,0x00000007be700000)
 ParOldGen       total 409600K, used 0K [0x000000079a800000, 0x00000007b3800000, 0x00000007b3800000)
  object space 409600K, 0% used [0x000000079a800000,0x000000079a800000,0x00000007b3800000)
 Metaspace       used 3387K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 376K, capacity 388K, committed 512K, reserved 1048576K
复制代码

逃逸分析:栈上分配,标量替换

OOM

Full GC中,元数据指向元数据的那些指针都不用再扫描了。不少复杂的元数据扫描的代码(尤为是CMS里面的那些)都删除了。 元空间只有少许的指针指向Java堆。这包括:类的元数据中指向java/lang/Class实例的指针;数组类的元数据中,指向java/lang/Class集合的指针。 没有元数据压缩的开销 减小了根对象的扫描(再也不扫描虚拟机里面的已加载类的字典以及其它的内部哈希表) 减小了Full GC的时间 G1回收器中,并发标记阶段完成后能够进行类的卸载

遇到 OOM 呢, 第一时间看内存分布,用工具 dump 出一张内存快照出来,工具备不少

  1. 先看是否是有不合理代码生成了大量对象而且这些对象内存泄露了,占用了大量内存出去
  2. 再看内存泄露,通常单单内存泄露不会 OOM,可是能够优化内存使用
  3. 增长屋里内存
  4. 看看是否是某些大致积对象声明周期过长,好比 bitmap

接口的匿名实现类其实是被做为一种类型来使用的,在每个匿名实现类在方法区都会占据一块 class 空间

StringTable

谁有error,谁有gc
相关文章
相关标签/搜索