简谈JVM

什么是JVM?      

JVM就是Java Virtual Mechine的缩写。它是一种基于计算设备的规范,是一台虚拟机,即虚构的计算机。 是通过在实际的计算机上仿真模拟各种计算机功能来实现的。是整个java实现跨平台的最核心的部分,能够运行以Java语言写作的软件程序。

JDK(Java Development Kit),是整个Java的核心,包括了Java运行环境JRE、Java工具和Java基础类库。 JRE(Java Runtime Environment)是运行JAVA程序所必须的环境的集合,包含JVM标准实现及Java核心类库。

                 

 JVM内存模型:

JAVA不需要像C/C++一样程序完全控制内存,使用内存直接new就可以,不需要销毁,由JVM来进行 清理,开发程序更简单,当然,在内存利用上更容易浪费。虚拟机会把它所管理的内存划分为多个不同 的数据区,称为运行时数据区。如下图:

方法区:存储虚拟机运行时加载的类信息、常量、静态变量和即时编译的代码,因此可以把这一部分考虑为 一个保存相对来说数据较为固定的部分,常量和静态变量在编译时就确定下来进入这部分内存,运行时类信 息会直接加载到这部分内存,所以都是相对较早期进入内存的。

 堆区:是JVM所管理的内存中最大的一块。主要用于存放对象实例,每一个存储在堆中的Java对象都会是这个对象的类的一个副本,它会复制包括继承自它父类的所有非静态属性。而所谓的垃圾回收也主要是在堆区进行。 根据Java虚拟机规范的规定,Java堆可以处于物理上不连续的内存空间中,只要逻辑是上连续的即可,就像我们的磁盘空间一样。在实现上,既可以实现成固定大小的,也可以是可扩展的。

JVM栈区(虚拟机栈区):主要是存放一些对象的引用和编译期可知的基本数据类型,这个区域是线程私有的, 即每个线程都有自己的栈,与线程生命周期相同。可能会出现两种异常情况: 如果线程请求的栈深度大于虚拟机锁所允许的深度,则抛出StackOverflowError异常 如果虚拟机栈可以动态扩展,扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。

本地方法栈:本地方法在运行时存储数据产生的栈区域。是JVM运行Native方法的空间,和JVM栈的作用是类似的,由于很多Native方法都是C语言实现的,所以它通常又叫C栈。和JVM栈一样,也会抛出StackOverflowError和OutOfMemoryError异常。

程序计数器:是用来记录程序运行到什么位置的,是线程私有的。

JVM内存回收机制:JVM区域分为堆区和非堆区,根据不同区域的特点采用不同的回收算法,从而提高垃圾回收效率。

垃圾回收器:主要回收的是堆区中未使用的内存区域,并对相应的区域进行整理。在堆区中,又根据对象内存的存活时间或者对象大小,分为“年轻代”和“年老代”。“年轻代”中的对象是不稳定的易产生垃圾,而“年老代”中的对象比较稳定,不易产生垃圾。

 JVM回收算法介绍:

一、引用计数式内存回收:

引用计数(Reference Count)式内存回收机制是Objective-C以及Swift语言中正在使用的内存回收机制。只要有引用,那么引用计数就加1。当引用计数为0时,该块内存就会被回收。这种内存清理方式容易形成“引用循环”。

在Objective-C的引用计数中循环引用而造成内存泄露的问题,可以将变量声明成weak或者strong类型。当出现“强引用循环”时,我们将其中的一个引用设置为weak类型即可,然后这种强引用循环就被打破了,也就不会造成“内存泄露”的问题。

二、复制式内存回收:

复制式回收:其核心就是“复制”,但前提是有条件复制。在垃圾回收时,将“活对象”复制到另一块空白的堆区,然后将之前的区域一并清除。“活对象”就是指沿着对象的引用链可以到“栈”上的对象。当然在将活对象复制到新的“堆区”后,也要将栈区的引用进行修改。 当内存垃圾特别多 ,垃圾回收的效率还是比较高的,因为复制的对象比较少,清除时直接将旧的堆空间进行清理即可。 当垃圾比较少的时候,复制大量的活对象,效率还是比较低的; 堆的存储空间进行分半,总有一半是空闲的,堆空间的利用率不高。

三、标记-压缩回收算法:

标记-压缩回收算法:在垃圾少时的工作效率比较高,而垃圾多的情况下,工作效率反而不高,这就与“复制式”形成了互补。 第一步就是标记,需要将堆区中的“活对象”进行标记 标记完成后,开始进行压缩了,将活对象压缩到“堆区”的一段,然后将剩余的部分进行清除。 如果垃圾太多碎片化严重时,移动的“活对象”较多,效率比较低。 这种方式可以与“复制式”结合使用,根据当前堆区的垃圾状态来选择哪种回收方式。正好与“复制式”形成优势互补。 将“复制式”、“标记-压缩式”的回收方式进行整合的算法,就是“分代式”垃圾回收机制。

四、分代式垃圾回收:

分代”即根据对象易产生垃圾的状态或者对象的大小将其分为不同的代,可分为“年轻代”、“年老代”和“永久代”(元空间)。元空间 不在堆中。不同代演变如下图:

JVM GC(Garbage Collection)触发条件:

堆内存GC  :      JVM使用较高的频率对年轻的对象(Young generation)进行YGC,而对老年代(Tenured Generation)较少(Tenured generation)进行Full GC。

非堆内存不GC  :     GC不会在主程序运行期对PermGen Space进行清理,所以如果你的应用中有很多CLASS(特别是动态生成类,当然permgen space存放的内容不仅限于类)的话,就很可能出现PermGen Space错误。

 

JVM 主要参数配置说明:

-server :服务器模式

-Xms:初始堆大小,默认为物理内存的1/64(<1GB);默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制,一般是直接设置为-Xmx大小,不需要再计算增加内存。

-Xmx:最大堆大小,默认(MaxHeapFreeRatio参数可以调整)空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制

-Xmn:新生代的内存空间大小,一般默认就好,推荐配置为整个堆的3/8,实际是多大,需要进行配置测试确定。

-XX:SurvivorRatio:新生代中Eden区域与Survivor区域的容量比值,默认值为8。两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10。

-XX:MetaspaceSize=8m    无数据配置(1.8后,取消了永久代)

-XX:MaxMetaspaceSize=50m  无数据配置

 JVM 配置说明:

-Xss:每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。应根据应用的线程所需内存大小进行适当调整。在相同物理内存下,大的应用建议使用256k。这个选项对性能影响比较大,需要严格的测试。

-XX:+HeapDumpOnOutOfMemoryError: 设置当首次遭遇内存溢出时导出此时堆中相关信息

-XX:HeapDumpPath: 指定导出堆信息时的路径或文件名,设置

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof,当发生内存溢出时,自动生成dump文件,可用于分析原因。 日志相关:

-Xloggc    :记录日志文件位置,如-Xloggc:/app/logs/system-center_.gclog

-XX:+PrintGCDateStamps:打印日志详情

-XX:+PrintGCApplicationStoppedTime:打印GC停顿时间

-XX:PrintHeapAtGC :打印GC前后的详细堆栈信息    

示例1: java -jar test.jar 示例2: java -server -Xms512m -Xmx512m -jar /app/erp/vms-storage.jar 示例3: java -server -Xms4G -Xmx4G -XX:+PrintGCDetails -XX:+PrintHeapAtGC -XX:+PrintGCDateStamps -Xloggc:/app/logs/syslog-center_.gclog -jar /app/erp/syslog-center.jar 示例4: java -server -Xms4G -Xmx4G -XX:+PrintGCDetails -XX:+PrintHeapAtGC -XX:+PrintGCDateStamps -Xloggc:/app/logs/syslog-center_.gclog   -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof -jar  /app/erp/syslog-center.jar

jstat命令使用:

jstat命令是使用频率比较高的命令,主要用来查看JVM运行时的状态信息,包括内存状态、垃圾回收等。

命令格式:

jstat [option] LVMID [interval] [count] 其中LVMID是进程id,interval是打印间隔时间(毫秒),count是打印次数(默认一直打印)

option参数解释:

-class class loader的行为统计

-compiler HotSpt JIT编译器行为统计

-gc 垃圾回收堆的行为统计

-gccapacity 各个垃圾回收代容量(young,old,perm)和他们相应的空间统计

-gcutil 垃圾回收统计概述

-gccause 垃圾收集统计概述(同-gcutil),附加最近两次垃圾回收事件的原因

-gcnew 新生代行为统计

-gcnewcapacity 新生代与其相应的内存空间的统计

-gcold 年老代和永生代行为统计

-gcoldcapacity 年老代行为统计

-gcpermcapacity 永生代行为统计

-printcompilation HotSpot编译方法统计

常用示例及打印字段解释:

字段解释:

S0 survivor0使用百分比

S1 survivor1使用百分比

E Eden区使用百分比

O 老年代使用百分比

M 元数据区使用百分比

CCS 压缩使用百分比

YGC 年轻代垃圾回收次数,回收一次大于0.1秒需要关注

YGCT 年轻代垃圾回收消耗时间

FGC 老年代垃圾回收次数,频繁FGC需要关注,最好的情况下是不出现,一般来说,一个小时出现一次可以接受。实际怎么样算频繁要看实际应用。

FGCT 老年代垃圾回收消耗时间

GCT 垃圾回收消耗总时间

jstack是用来查看JVM线程快照的命令,线程快照是当前JVM线程正在执行的方法堆栈集合。使用jstack命令可以定位线程出现长时间卡顿的原因,例如死锁,死循环等。jstack还可以查看程序崩溃时生成的core文件中的stack信息。

命令格式:

jstack [-l] <pid> (连接运行中的进程)

jstack -F [-m] [-l] <pid> (连接挂起的进程)

jstack [-m] [-l] <executable> <core> (连接core文件)

jstack [-m] [-l] [server_id@]<remote server IP or hostname> (连接远程debug服务器)

option参数解释: -F 当使用jstack <pid>无响应时,强制输出线程堆栈。

                            -m 同时输出java和本地堆栈(混合模式)

                            -l 额外显示锁信息

jmap是用来生成堆dump文件和查看堆相关的各类信息的命令,例如查看finalize执行队列,heap的详细信息和使用情况。

命令格式:

jmap [option] <pid> (连接正在执行的进程)

jmap [option] <executable <core> (连接一个core文件)

jmap [option] [server_id@]<remote server IP or hostname> (链接远程服务器)

option参数解释: <none> to print same info as Solaris pmap

                            -heap 打印java heap摘要

                            -histo[:live] 打印堆中的java对象统计信息

                            -clstats 打印类加载器统计信息

                           -finalizerinfo 打印在f-queue中等待执行finalizer方法的对象

                           -dump:<dump-options> 生成java堆的dump文件

                                            dump-options:

                                                        live 只转储存活的对象,如果没有指定则转储所有对象                                                                      format=b 二进制格式

                                                        file=<file> 转储文件到<file>

-F 强制选项  就是什么情况下都可以输出。

使用示例1 : jmap -heap 89917  //输出堆信息,如下图:

使用示例2: jmap -dump:format=b,file=test_data.dump 89917  //把内存输出到文件,用于分析

 举个栗子:

一个写数据库的应用,并发测试最高TPS 500多,需要进行优化。(在分析问题之前 一定要先确认环境是正常的!)

优化排过程: 1、先检查应用资源及数据库资源是否占用高;

                       2、检查GC情况;

                       3、资源正常,GC正常,性能上不去,就有可能是线程阻塞或者线程串行等待了,使用jstack查看线程状态 jstack 112594 >data1_test,输出到文件。查看BLOCKED线程;

                       4、很多问题在阻塞的,那么可能是什么原因呢?检查一下mysql线程池设计值,根据经验,mysql连接数在100-300性能最高。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值