JVM初窥

参考文档:

JVM配置官方文档:https://www.oracle.com/technetwork/java/javase/tech/exactoptions-jsp-141536.html

JVM官方FAQ:https://www.oracle.com/technetwork/java/hotspotfaq-138619.html

        内存是分管理的 ,或者是拥有不同年龄对象的内存池。当世代填满时,垃圾收集会在每个世代中发生。

        创建的新对象会被放入年轻代的eden空间,而年轻代gc采用复制算法,复制算法会把内存分为两个区域(即两个survivor空间:from和to)。当进行一次minor gc时(既年轻代的gc),minor gc是串行的,eden空间如果没有被gc root引用的会被回收,而依然存活的会被移动到from空间中,如果from空间在minor gc时对象依旧可以存活,就会对该对象年龄+1,当年龄达到一定数值时会直接放入老年代,没有达到年龄的存活对象会被复制到to中。这时from和eden空间已经被清空,虚拟机会交换from和to的空间,空的from变成to,to的变成from,保证了to是空的,minor gc会不断重复这样的工作,直到to彻底被填满,这时会将对象移动到老年代。
 

一、运行时数据区域

JVM包括:方法区、Java堆、本地方法栈、运行时常量池、程序计数器、虚拟机栈几部分。

方法区:

1、主要存储静态变量、常量、虚拟机加载的类信息、编译后的代码、final类型的常量、属性和方法信息。

2、JVM用持久代(Permanet Generation)来存放方法区,可通过-XX:PermSize和-XX:MaxPermSize来指定最小值和最大值。

3、如果分配不足会出现OutOfMemoryError异常。

Java堆:

1、存放new创建的对象;系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。一个JVM一个堆,线程共享。物理空间不连续,可扩展。

2、其大小可以通过-Xmx和-Xms来控制;

3、如果分配不足会出现OutOfMemoryError异常。

堆内存的划分:持久带+新生代

持久带:存放静态数据类型,如java class ,Method等,对垃圾回收器影响比较大,最易出现内存溢出。

新生代:old+new=(Eden,Survivor(from ,to))。

            1、年轻代对应Young GC,收集生命周期较短的对象,并在对应区域满时,移动到其他区域,直到移入老年代;

            2、老年代对应Full GC ,FullGC 耗时较长,应尽量减少,存放生命周期长的对象。

本地方法栈:

1、为本地方法服务。

2、在Windows下, 栈是向低地址扩展的数据结构,是一块连续的内存区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是固定的(是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。由系统自动分配,速度较快。但程序员是无法控制的。

3、抛出StackOverflow和OutOfMemoryError,

运行时常量池:

1、存放编译时的字面量和符号引用(类加载后),是方法区的一部分,受方法区内存限制,

2、动态性,运行时可将新常量放入池中;

3、抛出OutOfMemoryError

程序计数器:

1、线程私有,标记字节码文件的指令地址,生命周期随线程。

虚拟机栈:

1、存放局部变量表中的基本数据类型和对象引用、操作栈,动态链接‘方法出口’,包含栈帧。

2、执行java方法

3、抛出StackOverflow和OutOfMemoryError。

堆内存与栈内存需要说明:

基础数据类型直接在栈空间分配,方法的形式参数,直接在栈空间分配,当方法调用完成后从栈空间回收。引用数据类型,需要用new来创建,既在栈空间分配一个地址空间,又在堆空间分配对象的类变量 。方法的引用参数,在栈空间分配一个地址空间,并指向堆空间的对象区,当方法调用完成后从栈空间回收。局部变量new出来时,在栈空间和堆空间中分配空间,当局部变量生命周期结束后,栈空间立刻被回收,堆空间区域等待GC回收。方法调用时传入的literal参数,先在栈空间分配,在方法调用完成后从栈空间收回。字符串常量、static在DATA区域分配,this在堆空间分配。数组既在栈空间分配数组名称,又在堆空间分配数组实际的大小。

二、直接内存

1、不属于虚拟机内存中的定义部分,但是会受系统内存影响,会出现内存溢出异常。

 

三、OutOfMemoryError异常处理

1、初步根据错误查看程序配置的堆内存或计算机内存偏小,或者是由于频繁垃圾回收导致的异常;

2、针对java的堆内存溢出,可以使用可视化工具JConsole或VisualVM查看程序分析时的内存变化等情况,也可以根据官网文档配置垃圾垃圾回收的日志:

3、根据日志定位内存溢出的位置,重新分配对应区域的内存大小,同时检查代码中对应区域对象的应用等是否异常,对于代码中不在引用的对应或参数及时清理;

4、修改内存参数,根据官网提供的性能参数,选择合适的垃圾回收器并配置,重新监控内存分析。

 

四、JVM配置

垃圾回收主要分:串行,并行,CMS,G1回收器。

 

内存溢出的情况可以通过加上 -XX:+HeapDumpOnOutOfMemoryError 参数,该参数作用是:在程序内存溢出时输出 dump 文件。

有了 dump 文件,就可以通过 dump 分析工具进行分析了,比如常用的 MAT,Jprofile,jvisualvm 等工具都可以分析,这些工具都能够看出到底是哪里溢出,哪里创建了大量的对象等等信息。

-XX:+UseSerialGC:在新生代和老年代使用串休gc
-XX:+UseParNewGc:在新生代使用并行gc
-XX:+UseParallelOldGC:在老年代使用并行gc
-XX:ParallelGCThread:设置Parallel gc的垃圾回收线程数,通常与cpu数量相同
-XX:MaxGCPauseMillis:设置最大垃圾收集停顿时间,垃圾回收器会尽量控制回收的时间在该值范围内
-XX:GCPauseIntervalMillis:设置停顿时间间隔
-XX:GCTimeRatio:设置吞吐量大小,0~100之间的整数。若该值为n,那么jvm将会花费不超过1/(1+n)的时间用于垃圾回收。
-XX:+UseAdaptiveSizePolicy:开启自适应gc策略,jvm会根据运行时吞吐量等信息自动调整eden、old等空间大小以及晋升老年代年龄
-XX:+UseConcMarkSweepGC:新生代使用ParNew,老年代使用CMS和Serial,其中老年代的Serial用于作为CMS失败时调用的备选gc
-XX:+ParallelCMSThreads:CMS线程数量
-XX:CMSInitiatingOccupancyFraction:设置老年代空间被使用多少后触发CMS gc,默认为68%
-XX:CMSFullGCsBeforeCompaction:设置多少次CMS回收后,进行一次内存压缩
-XX:+CMSClassUnloadingEnabled:在类卸载后进行CMS回收
-XX:+CMSParallelRemarkEnabled:启用并行重标记
-XX:CMSInitiatingPermOccupancyFraction:当永久代空间被使用多少后触发CMS gc,百分比(在使用时CMSClassUnloadingEnabled必须被配置)
UseCMSInitiatingOccupancyOnly:只有当gc达到配置的阈值时才进行回收
XX:+CMSIncrementalMode:使用增量模式,适合单CPU
XX:+UserG1GC:使用G1回收器,与G1相关的虚拟机参数都只能在jdk1.7以上使用
XX:+UnlockExperimentalVMOptions:允许使用实验性参数
辅助设置:
-XX:+PrintGC:输出GC垃圾回收日志
-verbose:gc:与-XX:+PrintGC相同
-XX:+PrintGCDetail:输出详细的GC垃圾回收日志
-XX:+PrintGCTimeStamps:输出GC回收的时间戳
-XX:+PrintGCApplicationStoppedTIme:输出GC垃圾回收时所占用的停顿时间
-XX:+PrintGCApplicationConcurrentTime:输出GC并行回收时所占用的时间
-XX:+PrintHeapAtGC:输出GC前后详细的堆信息
-Xloggc:filename:把GC日志输出到filename指定的文件
-XX:+PrintClassHistogram:输出类信息
-XX:+PrintTLAB:输出TLAB空间使用情况
-XX:+PrintTenuringDistribution:输出每次minor GC后新的存活对象的年龄阈值

内存溢出的情况可以通过加上 -XX:+HeapDumpOnOutOfMemoryError 参数,该参数作用是:在程序内存溢出时输出 dump 文件。

有了 dump 文件,就可以通过 dump 分析工具进行分析了,比如常用的 MAT,Jprofile,jvisualvm 等工具都可以分析,这些工具都能够看出到底是哪里溢出,哪里创建了大量的对象等等信息。


在JDK1.8中,取消了PermGen,取而代之的是Metaspace,所以PermSize和MaxPermSize参数失效,取而代之的是

-XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=128m
 

内存信息打印:

处理器数量:"+Runtime.getRuntime().availableProcessors()+";总内存:"+Runtime.getRuntime().totalMemory()+";最大内存:"+Runtime.getRuntime().maxMemory()+ ";free 内存:"+Runtime.getRuntime().freeMemory() ;
 

附:类生命周期

加载--->验证--->准备---->解析---->初始化---->使用----->卸载

加载是通过类名获取类的二进制字节流文件,生成对应的类对象,验证是对语义规范,文件格式,字节码等信息的验证,准备是正式的为类分配内存,设置类变量的初始值,解析阶段是对类中的接口,参数及符号引用的解析,初始化类时先初始化父类,启动虚拟机时便会初始化main方法主类,然后在调用执行,对象不再使用或引用后卸载对象。

 

官方: https://www.oracle.com/technetwork/java/javase/gc-tuning-6-140523.html

参考:https://blog.csdn.net/csujiangyu/article/details/52071473

java线上排查:

https://mp.weixin.qq.com/s/NcdxE3u3WKt5fv7pb-kpsw

https://blog.csdn.net/lengyue309/article/details/80590119

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值