jvm调优

2024-01-01 07:10:09

?一 程序员的战场(内存空间)

? ? ? ? ?自从c与c++换成用java开发后发现写代码的速度大大提升而难度也大大的降低了,但发现对内存的管理开始模糊了,心里总有疑问写的东西运行时会占用了多少内存空间?内存占用空间可以说一直是对开发者非常重要,很多时候就觉得它就是战场,无论是什么开发语言都应该要撑控,而java开发由于是jvm管理内存,所以jvm调优应该每一个java程序员应该熟练的知识。

二 进行jvm调优要先知道代码所占用的内存

在Java中,查看一个变量、方法或类(对象)占用的内存空间并不直观,因为Java虚拟机(JVM)对内存分配是基于对象实例进行的。变量本身不直接占用内存,而是它们所引用的对象占用内存。以下是一些分析和估算的方法:

  1. 单个变量

    • 基本类型的变量(如int、char等)会占用固定的内存大小,与具体的值无关。
    • 对象引用类型的变量(如String、List等),该变量自身只存储一个指向对象的引用,其大小通常是固定的,通常为32位或64位环境下的4字节或8字节。
  2. 方法

    • 方法本身不占用运行时内存,它只存在于类的元数据中,执行方法时占用的是调用栈和方法内部创建的局部变量及临时对象的内存。
    • 如果想要了解方法执行过程中消耗了多少内存,可以通过性能分析工具(如VisualVM)监控堆内存变化,或者使用内存分析工具分析方法执行前后的堆转储文件,找出由该方法创建或影响的所有对象及其占用的内存。
  3. 类或对象占用的空间

    • 类实例占用的内存包括:
      • 对象头:包含类型指针、锁状态标志、哈希码等信息,大小与平台相关。
      • 实例字段:每个字段占用的空间取决于其类型。
      • 对齐填充:为了满足特定硬件平台上内存对齐的要求,可能会有额外的填充字节。
    • 使用java.lang.instrument.Instrumentation接口可以在运行时获取对象大小,但需要通过特殊的手段注入到应用中,例如前面提到的代理类方式。
    • 另外,可以使用第三方库如JOL (Java Object Layout) 来解析类的内存布局,但它并不会提供实际运行时的数据,而是给出理论上的内存布局。

?获取内存大小方式:

使用java.lang.instrument.Instrumentation接口获取Java对象的内存大小,首先需要创建一个代理类,并在JVM启动时通过 -javaagent 参数注入?,代码如下:

import java.lang.instrument.Instrumentation;

/**
* 获取内存大小
*/
public class MemoryAgent {
    private static volatile Instrumentation instrumentation;

    public static void premain(String agentArgs, Instrumentation inst) {
        instrumentation = inst;
    }

    public static long getObjectSize(Object obj) {
        if (instrumentation != null) {
            return instrumentation.getObjectSize(obj);
        } else {
            throw new IllegalStateException("未初始化!");
        }
    }
}

?编译该代理类并打包成jar文件(例如:memory-agent.jar)。

?在运行Java应用程序时,通过 -javaagent 参数指定代理类和jar包的位置:

java -javaagent:path/to/memory-agent.jar -jar your-application.jar

?在应用中,就可以调用MemoryAgent.getObjectSize()来获取对象的内存占用大小了

public class Main {
    public static void main(String[] args) {
        String testStr = "Hello, World!";
        
        // 获取字符串对象的内存占用大小
        long size = MemoryAgent.getObjectSize(testStr);
        System.out.println("The size of the string object is: " + size + " bytes");
    }
}

说明:getObjectSize()返回的是特定平台上的对象大小,包括对象头、实例数据以及对齐填充等部分,但不包括任何对象引用的对象本身所占用的内存。如果对象间存在复杂的引用关系,可能需要更复杂的分析工具来了解整个内存占用情况。?

三?JVM调优

JVM调优基础是指对Java虚拟机(JVM)进行性能优化的基本原理和步骤,它涉及到多个方面,包括但不限于内存管理、垃圾回收策略、线程管理和编译器优化等。以下是JVM调优的一些基本知识点:

  1. 内存管理与GC调优

    • 堆内存划分:Java堆内存通常被划分为新生代(Young Generation)、老年代(Old Generation)和元空间(Metaspace或PermGen在较早版本中)。
    • 内存分配策略:新创建的对象首先在Eden区分配,经过一定次数的Minor GC后,仍存活的对象会被晋升到老年代。
    • GC类型选择:根据应用特点选择合适的垃圾回收器,如Serial、Parallel、CMS、G1或者ZGC、Shenandoah等,并针对所选GC调整相关参数。
    • 调优指标:关注Full GC频率、STW时间、吞吐量以及内存碎片化等问题。
  2. JVM参数设置

    • -Xms?和?-Xmx:设定初始和最大堆大小。
    • -XX:NewRatio:控制新生代与老年代的比例。
    • -XX:SurvivorRatio:新生代中eden与survivor空间的比例。
    • -XX:+UseConcMarkSweepGC?或?-XX:+UseG1GC?等:指定垃圾收集器。
    • -XX:MaxTenuringThreshold:对象在新生代中经历多少次Minor GC后晋升至老年代的阈值。
  3. 监控与诊断工具

    • 使用jconsole、VisualVM、JMC等工具进行实时系统监控,查看CPU使用率、内存占用、线程状态、垃圾回收情况等。
    • 分析GC日志,了解GC事件的发生频率和影响。
  4. 代码优化

    • 降低对象分配频率和减少短生命周期对象的数量,避免过多的内存分配压力。
    • 合理设计数据结构以提高内存利用效率,比如尽量重用对象,减少临时对象的生成。
  5. 线程与并发优化

    • 调整线程池大小,避免过高的线程切换开销。
    • 优化锁机制,减少锁竞争,例如使用更高效的并发容器或无锁数据结构。
  6. JIT即时编译优化

    • 利用JVM的热点代码探测和即时编译功能来提升运行时性能。

常见的调优指标是降低全gc的次数,?涉及最常用的调优参数是-Xms?和?-Xmx。

总的来说,JVM调优是一个综合性的过程,需要结合具体应用场景、业务需求以及硬件资源状况,通过分析监控数据、合理配置JVM参数以及优化代码实现性能提升。调优过程中要注意权衡不同的性能指标,如响应时间、吞吐量和内存使用效率等。

文章来源:https://blog.csdn.net/qyhua/article/details/135320629
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。