JVM调优-代码层面与非代码层面

1. 代码层面

1.1. class文件视角看待

在java代码中,一行代码,在对应的class文件中对应的对象是多行的。

第一个例子:for循环

在开发过程中,例如在for循环,如果把对象放到for循环中,表面上,java代码对象并没有变多,但是在class文件里面对应的对象就变多了。

第二个例子,集合,比如创建list、map集合的时候,没有指定大小

它就采用默认的容量大小,但超过它的阈值时就会扩容,但是还有剩下的容量空间没有使用,就会被浪费了。如果一个list、map集合还好,如果是很多个,就会造成内存上的浪费。

可以在HeapHreo

第三个例子:初始化的懒加载

对象没有使用,就先不初始化

public class HeapUser {

    private  static List<User> userList = new ArrayList<>(5);

    public void getHeapUser() {
        userList.add(new User());
    }
}
public class HeapUser {
    private  static List<User> userList;
    public void getHeapUser() {
        if(userList == null) {
            userList = new ArrayList<>();
        }
        userList.add(new User());
    }
}
第四个例子:对象清理

对象清理,比如说 集合的清理,user.clear(),它并没有将内存清空,而是将元素清空,正确的方式是users =null;

设计模式、软件设计原则 数据结构与算法 这些是内功 ,用好后会让你的代码更加优雅

2. 非代码的参数

2.1. jvm命令

-XX:MaxTenuringThreshold = threshold

可以设置为10,5,6

不同的垃圾收集器,默认阈值是不一样的,比如默认垃圾收集器ParallelGC的阈值是15,CMS默认是6
//多大的对象会直接放到old区,而不会在Eden进行分配呢

//查看阈值大小,默认值为0
jinfo -flag PretenureSizeThreshold 进程id
//默认值为0,表示不管这个对象多大(不要超过整个Eden),都会默认放到Eden区

用jvm参数
-XX:PretenureSizeThreshold = 10 * 1024*1024  (大于10M的对象,放入到Old)
-Xmaxjitcodesize = 240m
不是所有的jvm都有简写的
//简写初始化堆内存大小
-Xms
//简写堆内存最大空间,超过这个限制就会报异常
-Xmx

这两个参数一般都是设置成一样大小的

2.2. OOM的维度

-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath= 保存路径 (D:\err.hprof)

获取到这个文件后交给对应的堆进行处理
  1. 连续分配对象得不到及时的回收
  2. 并发场景下,线程访问很多导致的OOM (Threadlocal)
  3. 有些资源没有得到及时的释放 (用finally{}释放)

2.3. CPU

计数机的资源:cpu 、内存、磁盘、网络

top -Hp PID  到底是进程最终哪个线程占cpu资源比较高
jstack PID | grep tid(1) 查询该线程详情

2.4. 大并发场景下

OOM ,可以通过调整新老年代的比例来完成调优,将新生代内存放大一些

但是在G1时,它不建议设置young区大小。具体如何选择,要根据实际的业务来说明。

2.5. 垃圾器调优

从两个维度,停顿时间、吞吐量方面调优,根据这两个选择合适的垃圾回收器。

评判一个垃圾处理器是好还是坏的标准是:
追求低停顿、高吞吐、少次数

以G1为例,

  1. 获取g1-gc.log文件
  2. 选择一款工具分析该日志
  3. 吞吐量和停顿时间,也是判断垃圾收集好坏的两个指标
  4. 分析数据
  • jvm参数

-Xms50M

-Xmx50M

吞吐量        停顿时间 			次数
96.977%			3.12ms			22
  • jvm参数

-Xms100M

-Xmx100M

吞吐量        停顿时间 			次数
97.29%			6.93ms			4

为什么堆空间变大后,这些数值会这样呢

次数:堆内存变大后,空间大,放得更多的对象,那么发生垃圾回收次数就变少了

停顿时间:堆内存变大后,空间大,放得更多的对象,需要处理的对象也变多,比之前更加耗时

吞吐量:任务执行时间time/(任务执行时间+垃圾对象回收时间time)。

C1提供了低停顿时间,但又不会太牺牲吞吐量。

C1还可以设置停顿时间目标:

//单位是毫秒
-XX:MaxGCPauseMillis=500

比如设置停顿时间目标为3

那么

吞吐量        停顿时间 			次数
96.39%			2.63ms			30
吞吐量        停顿时间 			次数
98%				3.21ms			18

之所以停顿时间那么少,是因为在筛选回收的环节

会按照Garbage-First: 有可能就有有一些region回收不到。

所以总结:就是停顿时间目标不要设置为太苛刻 ,就会导致收集的次数增加。

-XX:ComGCThreads = 3

默认是3,也可以添加线程数

吞吐量        停顿时间 			次数
97.83%			2.67ms			15

  • GC时机

虽然我们不用控制system.gc 但是可以设置g1 gc 触发时机的,基于堆内存使用率进行触发GC,例如G1是默认到达45%触发GC,如果觉得小了。

对于G1 的官方优化建议:

    1. 不用分配新老年代的比例,不管是直接指定大小,还是老年代比例,如果这样操作,就会破坏我们停顿时间的目标,
    2. 停顿时间的目标不要太严格,太严格会带来次数的增加
    3. 只有g1是有mixedGC的,其它处理器没有

那什么事mixed GC呢 ,

传统的GC:

要么发生young GC 要么发生old GC 要么发生全部的yangGC +old GC

mixedGC:

全部的youngGC + 部分的oldGC。

因此对于mixedGC的调优:
堆的使用比例,触发GC

2.6. GC次数太频繁怎么办

youngGC :

  1. 有没有可能young区空间不够了
  2. young空间是够的 ,但是Eden:S1:S0 的比例中,设置的Eden比例不合理,导致Eden区不够

old gc

  1. old区不够了
  2. 是不是频繁有young区对象晋升过来,导致old区很快就满了,需要gc (年龄阈值、大对象阈值)
  • 13
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值