jvm记录

JVM内存模型

1、方法区

2、堆

Java虚拟机将堆划分为新生代和老生代(1:2). 其中新生代还被划分为Eden区和两个大小相等的Survivor区

Java虚拟机采用动态分配的策略,根据生成对象的速率,以及Survivor区的使用情况动态调整Eden区和Survivor区的比例

通常当我们调用new指令时,它会在Eden区中划分一段内存来存储对象. 但是堆内存时线程共享的,因此在这里划分内存时需要同步的,否则会有多个对象公用一段内存的情况

对此Java虚拟机的做法是每个线程申请一段连续的内存,例如:2048字节,作为线程私有的TLAB; 这个操作需要加锁,线程需要维护两个指针,一个是直线TLAB中空余内存的起始位置,另一个指向TLAB的末尾

接下来new指令只需要通过指针加法(bump the pointer)来实现,即把指向空余内存的指针加上所请求的字节数; 当指针加法后指向空余内存的指针仍然小于指向末尾的指针,则分配内存成功;否则就失败,线程需要重新申请TLAB

当Eden区空间耗尽时,Java虚拟机会触发Minor GC,回收垃圾,存活下来的对象会移到Survivor区中

当Minor GC执行时:Eden区和from指向的Survivor区的存活对象会复制到to指针指向的Survivor,然后交换from和to指针的指向,保证to指针指向的Survivor区一定是空的

当一个对象被复制的次数达到15次时会被移动到老生代,或者当Survivor占用空间达到50%时,会将复制次数较高的对象移动到老生代

异常:

如果 Java 堆可以动态扩展,并且在尝试扩展的时候无法申请到足够的内存,那 Java 虚拟机将抛出一个 OutOfMemoryError 异常。

3、java方法栈

Java 虚拟机栈(Java Virtual Machine Stacks)是线程私有的,生命周期随着线程,线程启动而产生,线程结束而消亡。

Java 虚拟机栈描述的是 Java 方法执行的内存模型,用于存储栈帧。线程启动时会创建虚拟机栈,每个方法在执行时会在虚拟机栈中创建一个栈帧,用于存储局部变量表、操作数栈、动态连接、方法返回地址、附加信息等信息。每个方法从调用到执行完成的过程,就对应着一个栈帧在虚拟机栈中的入栈(压栈)到出栈(弹栈)的过程。

Java 虚拟机栈使用的内存不需要保证是连续的。

Java 虚拟机规范即允许 Java 虚拟机栈被实现成固定大小(-Xss),也允许通过计算结果动态来扩容和收缩大小。如果采用固定大小的 Java 虚拟机栈,那每个线程的 Java 虚拟机栈容量可以在线程创建的时候就已经确定。

异常:

  • 如果线程请求分配的栈容量超过了 Java 虚拟机栈允许的最大容量,Java 虚拟机将会抛出 StackOverflowError 异常。(死锁,方法内局部变量太多,方法调用太多)
  • 如果 Java 虚拟机栈可以动态扩展,并且在尝试扩展的时候无法申请到足够的内存,或者在创建新的线程时没有足够的内存去创建对应的虚拟机栈,那 Java 虚拟机将抛出一个 OutOfMemoryError 异常。

4、本地方法栈

本地方法栈与java方法的的区别在于本地方法栈是执行native代码的区域

5、PC寄存器

程序计数器是一个以线程私有的一块较小的内存空间,用于记录所属线程所执行的字节码的行号指示器;字节码解释器工作时,通过改变程序计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳准、异常处理、线程恢复等基础功能都需要依赖程序计数器来完成。

JVM判断对象可回收

1、引用计数器

引用计数法在每个对象中添加一个引用计数器,用来计算引用该对象的个数,当引用计数器归零时表明该对象已经死亡,可以被回收了.

问题:当两个对象相互引用时,类似构成死锁时,两个对象实际上已经死了,但是引用计数器没有归零,因此这两个对象将没有被回收

2、可达性分析

可达性分析算法是将一系列GC Roots 作为初始的存活对象集,从GC Roots出发探索能够被引用的对象加入存活对象集,而没有被引用的对象将会被标记,第一次标记时是死缓,如果在第二次标记时执行了finalize()方法,那么就逃脱死亡,否则就被回收.

初始的GC Root一般有以下:

    虚拟机栈上的本地变量表引用的对象
    方法区中类的静态属性引用的对象
    方法区中常量引用的对象
    本地方法栈中JNI引用的对象

可达性分析可以解决两个对象相互引用的问题,只要这两个对象不被GC Roots探索到就将被认为死亡.

垃圾回收算法

Mark-Sweep(标记-清除)

1、标记出所有待回收的对象

2、清除所有已标记的对象

缺点:容易产生内存碎片

Copying(复制)

将内存空间两等分,分别用from指针和to指针管理,当垃圾回收时,将存活对象复制到to区域,并且交换from和to的指向区域,这样也能解决内存碎片

缺陷:空间利用率低

Mark-Compact(标记-整理)算法<压缩法>

将存活对象后聚集到内存区域的起始位置,这样能够解决内存碎片

缺陷:有比较大的内存开销

Generational Collection(分代回收)算法

分代收集算法是目前大部分JVM的垃圾收集器采用的算法。它的核心思想是根据对象存活的生命周期将内存划分为若干个不同的区域。一般情况下将堆区划分为老年代(Tenured Generation)和新生代(Young Generation),老年代的特点是每次垃圾收集时只有少量对象需要被回收,而新生代的特点是每次垃圾回收时都有大量的对象需要被回收,那么就可以根据不同代的特点采取最适合的收集算法。

目前大部分垃圾收集器对于新生代都采取复制算法,因为新生代中每次垃圾回收都要回收大部分对象,也就是说需要复制的操作次数较少,但是实际中并不是按照1:1的比例来划分新生代的空间的,一般来说是将新生代划分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden空间和其中的一块Survivor空间,当进行回收时,将Eden和Survivor中还存活的对象复制到另一块Survivor空间中,然后清理掉Eden和刚才使用过的Survivor空间。

而由于老年代的特点是每次回收都只回收少量对象,一般使用的是标记-整理算法(压缩法)。

GC回收器

垃圾收集算法是 内存回收的理论基础,而垃圾收集器就是内存回收的具体实现。下面介绍一下HotSpot(JDK 7)虚拟机提供的几种垃圾收集器,用户可以根据自己的需求组合出各个年代使用的收集器。

回收器组合图

(A)、图中展示了7种不同分代的收集器:
       Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS、G1;
(B)、而它们所处区域,则表明其是属于新生代收集器还是老年代收集器:
      新生代收集器:Serial、ParNew、Parallel Scavenge;
      老年代收集器:Serial Old、Parallel Old、CMS;
      整堆收集器:G1;
(C)、两个收集器间有连线,表明它们可以搭配使用:
       Serial/Serial Old、Serial/CMS、ParNew/Serial Old、ParNew/CMS、Parallel Scavenge/Serial Old、Parallel Scavenge/Parallel Old、
(D)、其中Serial Old作为CMS出现"Concurrent Mode Failure"失败的后备预案(后面介绍);

垃圾回收器介绍

1.Serial/Serial Old收集器 是最基本最古老的收集器,它是一个单线程收集器,并且在它进行垃圾收集时,必须暂停所有用户线程。Serial收集器是针对新生代的收集器,采用的是Copying算法,Serial Old收集器是针对老年代的收集器,采用的是Mark-Compact算法。它的优点是实现简单高效,但是缺点是会给用户带来停顿。

2.ParNew收集器 是Serial收集器的多线程版本,使用多个线程进行垃圾收集。

3.Parallel Scavenge收集器 是一个新生代的多线程收集器(并行收集器),它在回收期间不需要暂停其他用户线程,其采用的是Copying算法,该收集器与前两个收集器有所不同,它主要是为了达到一个可控的吞吐量。

4.Parallel Old收集器 是Parallel Scavenge收集器的老年代版本(并行收集器),使用多线程和Mark-Compact算法。

5.CMS(Current Mark Sweep)收集器 是一种以获取最短回收停顿时间为目标的收集器,它是一种并发收集器,采用的是Mark-Sweep算法。主要分为4个步骤。

初始标记(CMS inital mark):需要“stop the world”,但只标记一下GC Roots能直接关联的对象,速度很快。

并发标记(CMS concurrent mark):是GC Roots Tracing的过程,花费时间长

重新标记(CMS remark):*需要“stop the world”,是为了修正并发标记期间因用户程序继续运行而导致标记产生变动的那一

分对象的标记记录,这个阶段时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。
并发清除(CMS concurrent sweep):是并发清除无用对象。

缺点:

CMS收集器对CPU资源非常敏感。在并发阶段,它虽然不会导致用户线程停顿,但是因为占用了一部分CPU资源而导致应用程序变慢,总吞吐量就会降低。CMS默认启动的回收线程数为(CPU数量+3)/4。当CPU的个数少于2个的时候,CMS对用户程序的影响可能会变得很大。

CMS收集器无法处理浮动垃圾(floating garbage),可能会出现concurrent mode failure导致另一次full gc的产生。在CMS的并发清理阶段,由于程序还在运行,垃圾还会不断产生,这一部分垃圾出现在标记过程之后,CMS无法在本次收集中处理掉它们,只好留到下一次GC再处理。这种垃圾称为浮动垃圾。同样由于CMS GC阶段用户线程还需要运行,即还需要预留足够的内存空间供用户线程使用,因此CMS收集器不能像其他收集器那样等到老年代几乎完全被灌满了再进行收集而需要预留一部分空间提供并发收集时的程序运作使用。默认设置下 CMS收集器在老年代使用了68%的空间后就会被激活。这个值可以用-XX:CMSInitiatingOccupancyFraction来设置。要是CMS运行期间预留的内存无法满足程序需要,就会出现concurrent mode failure,这时候就会启用Serial Old收集器作为备用进行老年代的垃圾收集。

空间碎片过多(标记-清除算法的弊端),CMS是基于标记-清除算法来实现的回收器,提供-XX:+UseCMSCompactAtFullCollection参数,应用于在FULL GC后再进行一个碎片整理过程。-XX:CMSFullGCsBeforeCompaction,多少次不压缩的full gc后来一次带压缩的。

6.G1收集器(Garbage-First):是当今收集器技术发展的最前沿的成果之一,G1是一款面向服务器端应用的垃圾收集器。 使用G1收集器时,java堆的内存布局就与其他收集器有很大差别,它将真个java堆划分为多个大小相等的独立区域(Region),虽然还保留新生代与老年代的概念,但新生代与老年代不再试物理隔离的了,他们都是一部分Region(不需要连续)的集合。G1具备如下特点:

并行与并发:G1能充分利用多CPU、多核环境下的硬件优势,使用多个CPU(CPU或者CPU核心)来缩短Stop-The-World停顿的时间,部分其他收集器原本需要停顿java线程执行的GC动作,G1收集器仍然可以通过并发的方式让java程序继续执行。
分代收集:与其他收集器一样,分代概念在G1中依然得以保留。虽然G1可以不需要其他收集器配合就能够独立管理整个GC堆,但它能够采用不同的方式去处理新创建的对象和已经存活了一段时间、熬过多次GC的旧对象以获取更好的收集效果。
空间整合:与CMS的“标记–清理”算法不同,G1从整体来看是基于“标记–整理”算法实现的收集器,从局部(两个Region之间)上来看是基于“复制”算法实现的,但无论如何,这两种算法都意味着G1运行期间不会产生内存空间碎片,收集后能提供规整的可用内存。这个特性有利于程序长时间运行,分配大对象时不会因为无法找到连续内存空间而提前出发下一次GC。
可预测的停顿:这是G1相对于CMS的另一大优势,降低停顿时间是G1和CMS共同的关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒,这几乎已经是实时java(RTSJ)的垃圾收集器的特性了。

初始标记(Initial Marking):标记GC Roots能够直接关联到的对象,并且修改TAMS的值,能在正确可用的Region中创建对象,这阶段需要停顿线程,而且耗时很短。
并发标记(Concurrent Marking):从GC Roots开始堆中对象进行可达性分析,找出存活的对象,这个时间耗时比较长,但可与用户程序并行执行。
最终标记(Final Marking):为了修正和正在并发标记期间因用户程序继续运行而导致标记产生变动的那一部分没有标记记录,虚拟机将这一段对象变法记录在线程Rememberred Set logs里面,最终标记阶段需要把Remembered Set logs 的数据合并到Remembered Set中,这阶段需要停顿线程,但是可并发执行。
筛选回收(Live Data Counting and Evacuation):对各个Region的回收截止和成本进行排序,根据用户期望的GC停顿时间来制定回收计划,这阶段可以做到和用户程序一起并发执行,但是因为值回收一部分Region,时间是用户可控制的,而且停顿用户线程将大幅度提高手机效率。

JVM参数设置分析

内存相关

参数名称含义默认值 
-Xms初始堆大小物理内存的1/64(<1GB)默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制.
-Xmx最大堆大小物理内存的1/4(<1GB)默认(MaxHeapFreeRatio参数可以调整)空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制
-Xmn年轻代大小(1.4or lator) 注意:此处的大小是(eden+ 2 survivor space).与jmap -heap中显示的New gen是不同的。
整个堆大小=年轻代大小 + 年老代大小 + 持久代大小.
增大年轻代后,将会减小年老代大小.此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8
-XX:PermSize设置持久代(perm gen)初始值物理内存的1/64 
-XX:MaxPermSize设置持久代最大值物理内存的1/4 
-Xss每个线程的堆栈大小 JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K.更具应用的线程所需内存大小进行 调整.在相同物理内存下,减小这个值能生成更多的线程.但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右
一般小的应用, 如果栈不是很深, 应该是128k够用的 大的应用建议使用256k。这个选项对性能影响比较大,需要严格的测试。(校长)
和threadstacksize选项解释很类似,官方文档似乎没有解释,在论坛中有这样一句话:"”
-Xss is translated in a VM flag named ThreadStackSize”
一般设置这个值就可以了。
-XX:SurvivorRatioEden区与Survivor区的大小比值 设置为8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10,1个Survivor:Eden=1:8
-XX:SurvivorRatio新生代(Eden + 2*S)与老年代(不包括永久区)的比值 设置为4,表示新生代 :老年代 = 1:4 ,意思是老年代占 4/5

demo

JAVA_OPTIONS="-server -Xmn6G -Xms16G -Xmx16G -XX:PermSize=512M -XX:MaxPermSize=512M -Xss256k -Xconcurrentio -XX:SurvivorRatio=5 -XX:TargetSurvivorRatio=90 -XX:MaxTenuringThreshold=31 -XX:CMSInitiatingOccupancyFraction=90 -XX:MaxDirectMemorySize=256M -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseConcMarkSweepGC -Djava.nio.channels.spi.SelectorProvider=sun.nio.ch.EPollSelectorProvider -Dsun.net.inetaddr.ttl=60 -Dorg.mortbay.jetty.Request.maxFormContentSize=-1 -Djava.awt.headless=true -Dsolr.solr.home=/home/xiaoi/jetty_search_6600/solr_home/solr -XX:-DontCompileHugeMethods -Xloggc:/home/xiaoi/gc_6600.log -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/xiaoi/dump_logs_search/"

线上问题排查命令

df -h排查磁盘空间,要注意挂载点的区别
free -g/m查询当前内存空间,以G或者M为单位

sync;

echo 3 > /proc/sys/vm/drop_caches;

echo 0 > /proc/sys/vm/drop_caches;

内存释放,当linux内核版本为3系列时不能够执行echo 0 > /proc/sys/vm/drop_caches;
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'查看tcp连接状态
ulimit -a / ulimit -u 102400 / ulimit -n 102400查看系统资源限制以及修改进程/文件大小限制
topShift+p 按CPU占用率倒序 Shift+m 按内存占用率倒序

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值