Literature will be a JVM performance optimization

Actual performance optimization

1 re-cognition JVM

Before we painted a picture, from Class file to the class loader, then the process runtime data area, and now we put this figure might look rich sound, showing the general physical structure of the JVM.

 

Execution engine: JVM byte code instructions for executing the

Mainly by two ways:

(1) the input bytecode instructions translated into another virtual machine instruction when loading or execution;

(2) the input bytecode instructions translated into a host instruction set of CPU local host when loading or execution. Both methods correspond to the interpreted bytecode and time compilation.

9.2 heap overflow

9.2.1 Code

 

 

Remember to set parameters such -Xmx20M -Xms20M

9.2.2 operating results

Visit -> http: // localhost: 8080 / heap

Exception in thread "http-nio-8080-exec-2" java.lang.OutOfMemoryError: GC overhead limit exceeded

9.2.3 Review and jinfo jps

 

 

9.2.4 Review jmap parameters manually export and automatic export

 jmap manually export: jmap -dump: format = b, file = heap.hprof PID

 

 

Auto Export parameters:

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heapdump.hprof

 

9.3 Method memory overflow area

Class such as adding information to a method of zone

9.3.1 asm dependent and Class Code

 

 

9.3.2 Code

 Metaspace size setting, such as -XX: MetaspaceSize = 50M -XX: MaxMetaspaceSize = 50M

9.3.3 operating results

Visit -> http: // localhost: 8080 / nonheap

 

 

9.4 virtual machine stack

9.4.1 code demonstrates StackOverFlow

  

 

9.4.2 operating results

 

9.4.3 Description

Stack Space用来做方法的递归调用时压入Stack Frame(栈帧)。所以当递归调用太深的时候,就有可能耗尽Stack Space,爆出StackOverflow的错误。

-Xss128k:设置每个线程的堆栈大小。JDK 5以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。根据应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。

线程栈的大小是个双刃剑,如果设置过小,可能会出现栈溢出,特别是在该线程内有递归、大的循环时出现溢出的可能性更大,如果该值设置过大,就有影响到创建栈的数量,如果是多线程的应用,就会出现内存溢出的错误。

9.5 线程死锁

9.5.1 代码

 

 

9.4.2 运行结果

 

9.4.3 jstack分析

 

把打印信息拉到最后可以发现

 

9.4.4 jvisualvm

 

将线程信息dump出来

 

9.6 垃圾收集

内存被使用了之后,难免会有不够用或者达到设定值的时候,就需要对内存空间进行垃圾回收。

9.6.1 垃圾收集发生的时机

GC是由JVM自动完成的,根据JVM系统环境而定,所以时机是不确定的。

当然,我们可以手动进行垃圾回收,比如调用System.gc()方法通知JVM进行一次垃圾回收,但是具体什么时刻运行也无法控制。也就是说System.gc()只是通知要回收,什么时候回收由JVM决定。

但是不建议手动调用该方法,因为消耗的资源比较大。

一般以下几种情况会发生垃圾回收

(1)当Eden区或者S区不够用了

(2)老年代空间不够用了

(3)方法区空间不够用了

(4)System.gc()

虽然垃圾回收的时机是不确定的,但是可以结合之前一个对象的一辈子案例,文字图解再次梳理一下堆内存回收的流程。

一个对象的一辈子

我是一个普通的Java对象,我出生在Eden区,在Eden区我还看到和我长的很像的小兄弟,我们在Eden区中玩了挺长时间。

有一天Eden区中的人实在是太多了,我就被迫去了Survivor区的“From”区,自从去了Survivor区,我就开始漂了,有时候在Survivor的“From”区,有时候在Survivor的“To”区,居无定所。直到我18岁的时候,爸爸说我成人了,该去社会上闯闯了。

于是我就去了年老代那边,年老代里,人很多,并且年龄都挺大的,我在这里也认识了很多人。在年老代里,我生活了20年(每次GC加一岁),然后被回收。

 

9.6.2 实验环境准备

我的本地机器使用的是jdk1.8和tomcat8.5,大家也可以使用linux上的tomcat,然后把gc日志下载下来即可。

9.6.3 GC日志文件

回顾升华一下垃圾收集器图

 

要想分析日志的信息,得先拿到GC日志文件才行,所以得先配置一下,之前也看过这些参数。

-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:$CATALINA_HOME/logs/gc.log

比如打开windows中的catalina.bat,在第一行加上

set JAVA_OPTS=%JAVA_OPTS% -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:gc.log 

这样使用startup.bat启动tomcat的时候就能够在当前目录下拿到gc.log文件

可以看到默认使用的是ParallelGC

9.6.3.1 Parallel GC日志

【吞吐量优先】

2019-06-10T23:21:53.305+0800: 1.303: [GC (Allocation Failure) [PSYoungGen: 65536K[Young区回收前]->10748K[Young区回收后](76288K[Young区总大小])] 65536K[整个堆回收前]->15039K[整个堆回收后](251392K[整个堆总大小]), 0.0113277 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 

`注意`如果回收的差值中间有出入,说明这部分空间是Old区释放出来的

 

9.6.3.2 CMS日志

【停顿时间优先】

参数设置

-XX:+UseConcMarkSweepGC

重启tomcat获取gc日志,这里的日志格式和上面差不多,不作分析。

9.6.3.3 G1日志

G1日志格式参考链接:

https://blogs.oracle.com/poonam/understanding-g1-gc-logs

【停顿时间优先】

why?

https://blogs.oracle.com/poonam/increased-heap-usage-with-g1-gc

参数设置

-XX:+UseG1GC

 

9.6.4 GC日志文件分析工具

9.6.4.1 gceasy

可以比较不同的垃圾收集器的吞吐量和停顿时间

 

 

9.6.4.2 GCViewer

 

9.6.5 G1调优

是否选用G1垃圾收集器的判断依据

https://docs.oracle.com/javase/8/docs/technotes/guides/vm/G1.html#use_cases

(1)50%以上的堆被存活对象占用

(2)对象分配和晋升的速度变化非常大

(3)垃圾回收时间比较长

(1)使用G1GC垃圾收集器: -XX:+UseG1GC

修改配置参数,获取到gc日志,使用GCViewer分析吞吐量和响应时间

Throughput       Min Pause       Max Pause      Avg Pause       GC count  

         99.16%         0.00016s         0.0137s        0.00559s          12

(2)调整内存大小再获取gc日志分析

-XX:MetaspaceSize=100M-Xms300M-Xmx300M

比如设置堆内存的大小,获取到gc日志,使用GCViewer分析吞吐量和响应时间

Throughput       Min Pause       Max Pause      Avg Pause       GC count

  98.89%             0.00021s             0.01531s            0.00538s             12

 (3)调整最大停顿时间

-XX:MaxGCPauseMillis=200    设置最大GC停顿时间指标

比如设置最大停顿时间,获取到gc日志,使用GCViewer分析吞吐量和响应时间

Throughput       Min Pause       Max Pause      Avg Pause       GC count

        98.96%          0.00015s        0.01737s       0.00574s          12

(4)启动并发GC时堆内存占用百分比

-XX:InitiatingHeapOccupancyPercent=45 G1用它来触发并发GC周期,基于整个堆的使用率,而不只是某一代内存的使用比例。值为 0 则表示“一直执行GC循环)'. 默认值为 45 (例如, 全部的 45% 或者使用了45%).

比如设置该百分比参数,获取到gc日志,使用GCViewer分析吞吐量和响应时间

Throughput       Min Pause       Max Pause      Avg Pause       GC count

        98.11%          0.00406s        0.00532s       0.00469s          12

9.6.6 G1调优的最佳实践

官网建议:

(https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/g1_gc_tuning.html#recommendations)

(1)不要手动设置新生代和老年代的大小,只要设置整个堆的大小

  • G1收集器在运行过程中,会自己调整新生代和老年代的大小

  • 其实是通过adapt代的大小来调整对象晋升的速度和年龄,从而达到为收集器设置的暂停时间目标

  • 如果手动设置了大小就意味着放弃了G1的自动调优

(2)不断调优暂停时间目标

一般情况下这个值设置到100ms或者200ms都是可以的(不同情况下会不一样),但如果设置成50ms就不太合理。暂停时间设置的太短,就会导致出现G1跟不上垃圾产生的速度。最终退化成Full GC。所以对这个参数的调优是一个持续的过程,逐步调整到最佳状态。暂停时间只是一个目标,并不能总是得到满足。

(3)使用-XX:ConcGCThreads=n来增加标记线程的数量

IHOP如果阀值设置过高,可能会遇到转移失败的风险,比如对象进行转移时空间不足。如果阀值设置过低,就会使标记周期运行过于频繁,并且有可能混合收集期回收不到空间。 

> IHOP值如果设置合理,但是在并发周期时间过长时,可以尝试增加并发线程数,调高ConcGCThreads。

(4)MixedGC调优 

-XX:InitiatingHeapOccupancyPercent

-XX:G1MixedGCLiveThresholdPercent

-XX:G1MixedGCCountTarger

-XX:G1OldCSetRegionThresholdPercent

(5)适当增加堆内存大小

9.7 一张图总结JVM性能优化

 

全文完!thanks for watching

相关阅读:

面试JVM,把这些说给面试官,会对你刮目相看!

看完这篇文章,我奶奶都知道JVM中的内存模型与垃圾回收了!

一文学会JVM配置参数与工具使用

Guess you like

Origin www.cnblogs.com/xueSpring/p/11611541.html