JVM性能优化,拿来吧你!

在这里插入图片描述
对于 Java 开发的同学来说,JVM 性能优化可以说是比较难掌握的知识点。这不仅因为 JVM 性能优化需要掌握晦涩难懂的 JVM 知识,还因为 JVM 性能优化很难有使用场景。这导致了许多人对 JVM 性能优化不熟悉,感觉就像是空中楼阁的天物一样不可触及。这几天工作中做了一次 JVM 性能优化,我想这对于 JVM 调优的初学者会有较大帮助。

背景

我们都知道 JVM 分为了新生代和老年代,并且我们在启动应用的时候都会配置对应的参数,为应用程序运行的 JVM 调整内存大小。但我们都知道,很多时候我们都只是大致估计一个数,随便填填,然后就上线了。

作者所在的公司同样存在这种情况,JVM 内存大小基本上都设得挺大的,毕竟内存大总比内存溢出好,因此就造成了不少的内存浪费。所以作者收到的任务就是对所有的应用进行一次排查,调整合适的内存参数,优化 JVM 的性能。

调优实战

要对应用进行 JVM 性能调优,那么首先得知道其运行的情况。这就像去医院看医生,去开药之前需要医生先望闻问切一样。在 Java 中,有很多方式可以观察到 JVM 的内部情况,例如 JDK 提供的各种命令工作。作者所在公司使用的是 Prometheus 进行监控,因此我们可以直接在 Prometheus 上看到应用的 JVM 运行情况。
应用层面优化
除了 GC 频率、GC 停顿时间,我们还能从应用的类型来分析 JVM 的内存消耗情况。

例如对于接口类型的系统来说,很多请求都是 1 秒中之内就结束。对于这种类型的请求,他们进入应用时会分配内存,结束时内存就会立刻被回收,留存下来的对象很少。这种应用的 JVM 内存情况大概是这样的:新生代消耗比较大,并且随着周期性回收内存,但老年代的内存消耗则更小。对于那些持续性处理的应用,例如持续时间长的应用处理。因为其存活时间较久,所以可能会有更多的对象晋升到老年代,因此老年代的内存消耗就比较大。

通过观察 JVM 年轻代与老年代的内存消耗情况,再结合应用本身的特性,我们可以发现应用中不合理的地方,再对应用进行针对性的优化。例如:应用某个地方每次都会存储大量的临时数据到内容中,这样就造成了 JVM 可能爆发 GC,从而导致应用卡顿。

小总结

总结一下本篇文章的调优方法: 通过观察 GC 频率和停顿时间,来进行 JVM 内存空间调整,使其达到最合理的状态。调整过程记得小步快跑,避免内存剧烈波动影响线上服务。 这其实是最为简单的一种 JVM 性能调优方式了,可以算是粗调吧。但 JVM 性能调优还有更多、更详细的参数,后续有机会我们再聊聊。

此外,通过观察 JVM 年轻代与老年代的情况,也可以帮助我们对应用进行针对性的优化,从而提升应用本身的性能。

一、内存溢出

内存溢出的原因:程序在申请内存时,没有足够的空间。

1. 栈溢出

方法死循环递归调用(StackOverflowError)、不断建立线程(OutOfMemoryError)。

2. 堆溢出

不断创建对象,分配对象大于最大堆的大小(OutOfMemoryError)。

3. 直接内存

JVM 分配的本地直接内存大小大于 JVM 的限制,可以通过-XX:MaxDirectMemorySize 来设置(不设置的话默认与堆内存最大值一样,也会出现OOM 异常)。

4. 方法区溢出

一个类要被垃圾收集器回收掉,判定条件是比较苛刻的,在经常动态生产大量 Class 的应用中,CGLIb 字节码增强,动态语言,大量 JSP(JSP 第一次运行需要编译成 Java 类),基于 OSGi 的应用(同一个类,被不同的加载器加载也会设为不同的类),都可能会导致OOM。

二、内存泄露

程序在申请内存后,无法释放已申请的内存空间,导致这一部分的原因主要是代码写的不合理,比如以下几种情况。

1. 长生命周期的对象持有短生命周期对象的引用

例如将 ArrayList 设置为静态变量,然后不断地向ArrayList中添加对象,则 ArrayList 容器中的对象在程序结束之前将不能被释放,从而造成内存泄漏。

2. 连接未关闭

如数据库连接、网络连接和 IO 连接等,只有连接被关闭后,垃圾回收器才会回收对应的对象。

3. 变量作用域不合理

例如:

一个变量的定义的作用范围大于其使用范围。
如果没有及时地把对象设置为 null。

4. 内部类持有外部类

Java 的 非静态内部类 的这种创建方式,会隐式地持有外部类的引用,而且默认情况下这个引用是强引用,因此,如果内部类的生命周期长于外部类的生命周期,程序很容易就产生内存泄露(可以理解为:垃圾回收器会回收掉外部类的实例,但由于内部类持有外部类的引用,导致垃圾回收器不能正常工作)。

解决办法:将非静态内部类改为 静态内部类,即加上 static 修饰,例如:

public class Jvm5 {
   
    private static String string = "SuunyBear";

    public static void show() {
   
        System.out.println("show");
    }

    public static void main(String[] args
  • 10
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 22
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

牛战士从不脱下面具

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值