jvm 内存机制
jvm 的内存包括stack ,Heap,NonHeap,在此重点说明Heap,NonHeap。
Heap叫堆内存,它用于存放类实例和数组信息。NonHeap叫非堆内存,用于存放类,方法等可反射的对象以及类静态变量,常量池等。
Heap包含年轻态,年老态。NonHeap包含永久态。年轻态包含1个eden去和2个survivor区。
内存区域的大小均有相应的参数配置。各个参数详解如下:
a) 堆内存设置:程序可以操作的。
-Xms 初始化堆内存,默认是物理内存的1/64。当空余堆内存小于40%时,会增加到-Xmx的最大限制。
-Xmx 最大堆内存分配,默认是物理内存的1/4,当空余内存大于70%时,会减少到-Xms的最大限制。
一般,Xmx 和Xms 一般大小相等。
b)非堆内存设置
-XX:PermSize 初始化非堆内存,默认是物理内存的1/64。
-XX:MaxPermSize 非堆内存最大值,默认是物理内存的1/4。
申请内存的过程:
1.jvm优先分配在eden区,若eden空间足够,则内存申请结束。
2.否则jvm试图释放eden区的不活跃的对象(minor gc),释放后若eden仍不足放入新的对象,则jvm试图将部分eden区活跃对象放入survivor区。
3.若survivor不足以存放且old区有足够的空间存放,则直接存放到old区并将survivor区的对象移到old区。
4.若old空间不足,则进行full gc。
5.若回收后survivor及old区仍不足以存放从Eden复制过来的部分对象,导致jvm无法在eden区为新对象分配内存,则出现OOM错误。
GC内存回收的机制:
jvm中,程序计数器,虚拟机栈,本地方法栈都是随着线程而生随线程而灭,实现了自动的内存清理,因此,我们 的垃圾回收主要集中于java堆和方法区中,在程序运行期间,这部分的内存的分配和使用是动态的。
对象存活判断:可达性分析,从root向下搜索,搜索走过的路径称为引用链,当一个对象到gc root 没有任何引用链相连时,则证明此对象是不可用的。其中 gc root包括:
虚拟机栈中引用的对象。方法区中引用的对象,本地方法栈中JNI引用的对象
gc算法包括以下几种:标记-清除,复制算法,标记-压缩算法
基于算法实现的垃圾收集器:serial收集器(新生代-复制算法,老年代-标记压缩,都串行),ParNew收集器(新生代并行,老年代串行),Parallel收集器(新生代并行,老年代串行,可打开自适应调节策略,以提供最合适的停顿时间或最大的吞吐量),Parallel Old收集器(新生代和老年代均并行),CMS收集器,G1收集器。
其中CMS有5个步骤:初始标记,并发标记,重新标记,并发清除,并发重置,第1和3个步骤有短暂的暂停,其余步骤和应用线程并发执行。
优点:并发收集、低停顿
缺点:产生大量控件碎片,并发阶段会降低吞吐量
JVM内出现OOM,查找步骤如下:
1.确认是不是内存本身就分配过小
jmap -heap pid(pid是进程的id),该工具可以查出新生代,年老代堆内存的分配大小及使用情况
2.找到最耗内存的对象
jmap -histo:live pid|more
改命令可以找到各个存活对象的数目和大小,但是,该执行该命令会执行一次fgc,可以执行该命令后dump内存,通过mat分析(如visualVm,jprofiler,JProbe等)
jvm调优的jstack用法
这个命令多用于jvm调优,具体方式如下:
1. 通过ps找到对应进程的pid ps -ef|grep tomcat
2. ps -mp pid -o THREAD,tid,time 找到该进程下所有的线程,并可以发现耗时或耗cpu的线程id
3. 通过printf '%x' tid 将十进制数据转换为十六进制 如:7b8a
4. jstack pid >> /tmp/mp.txt
5. 通过grep 7b8a /tmp/mp.txt -A 10 找到该线程对应的具体代码信息