JVM调优一期


**前言:**JVM一般不会进行调优,性能不够时一般选择在集群中加机器或优化代码,此次培训会讲一些JVM的基础知识,及通过了解JVM的运行机制,结合后续的性能测试调整常用JVM参数、规范代码中全局变量、大对象、线程池等的使用,避免出现内存溢出的问题。

一、JVM概述

JVM概述

二、性能优化涉及的知识点

在这里插入图片描述

三、内存的分配和回收

1、内存分配的过程简述

对于Person person = new Person();:

**内存分配:**一旦 Person 类加载完成,JVM会在堆内存中为 Person 对象分配内存空间。这个内存空间的大小取决于 Person 对象的成员变量所占用的空间。//memory = allocate();

**对象初始化:**分配内存后,JVM会调用 Person 类的构造函数(如果有的话)来初始化 Person 对象。构造函数会对 Person 对象进行初始化,例如设置对象的初始状态和属性值。//instance(memory)

**对象引用:**最后,JVM会返回一个指向新创建的 Person 对象的引用,并将其赋给 person 变量。通过这个引用,我们可以通过 person 变量来访问和操作 Person 对象的属性和方法。//instance = memory

在这里插入图片描述

2、垃圾回收是什么?

Java虚拟机(JVM)的垃圾回收是一种自动管理内存的机制,用于回收不再使用的对象并释放它们占用的内存空间,从而避免内存泄漏和内存溢出问题。垃圾回收的主要目标是优化内存利用,提高系统性能,以及减少开发人员手动管理内存的工作量。

在Java中,垃圾回收器负责扫描堆内存中的对象,标记出不再被引用的对象,然后释放这些对象占用的内存空间。垃圾回收过程分为以下几个步骤:

标记(Marking):首先,垃圾回收器会从根对象(如全局变量、栈中的引用等)开始,递归地遍历所有可达对象,并标记为存活对象。

清除(Sweeping):标记完成后,垃圾回收器会扫描整个堆内存,清除所有未被标记的对象,即被标记为垃圾的对象。

回收(Collection):在清除阶段之后,垃圾回收器会将清除的内存空间进行整理,以便后续的内存分配。

3、回收哪些对象?

堆中几乎放着所有的对象实例,对堆垃圾回收前的第一步就是要判断哪些对象已经死亡(即不能再被任何途径使用的对象)。

1)、引用计数器

给对象添加一个引用计数器,每当有一个地方引用它,计数器就加一,
当引用失效,计数器就减一,
任何时候计数器为0的对象就是不可能再被使用的。
这种方式实现简单,效率也高、但是目前主流的虚拟机中并没有选择这种算法,其主要原因是因为它很难解决对象之间相互循环引用的问题。
所谓的对象之间相互引用问题,可以参考下面代码来了解;

除了对象objA 和 objB 相互引用着对方之外,这两个对象之间再无任何引用。但是他们因为互相引用对方,导致它们的引用计数器都不为0,于是引用计数算法无法通知 GC 回收器回收他们。

public class ReferenceCountingGc {
   Object instance = null;

   public static void main(String[] args) {
      ReferenceCountingGc objA = new ReferenceCountingGc();
      ReferenceCountingGc objB = new ReferenceCountingGc();
      objA.instance = objB;
      objB.instance = objA;
      objA = null;
      objB = null;
   }
}

2)、可达性分析算法

将“GC Roots” 对象作为起点,从这些节点开始向下搜索引用的对象,找到的对象都标记为非垃圾对象,其余未标记的对象都是垃圾对象

在这里插入图片描述

4、回收的优先级?

WeakHashMap源码解析及使用场景

强引用(Strong Reference):

  • 强引用是最常见的引用类型,通常以 Object obj = new Object(); 的形式创建。

  • 当存在强引用指向一个对象时,垃圾回收器不会回收该对象,即使内存不足也不会回收。只有当所有强引用都被移除时,对象才会被垃圾回收器回收。

软引用(Soft Reference):

  • 软引用是比强引用弱一些的引用类型。可以通过 SoftReference 类来创建软引用。

  • 当系统内存不足时,垃圾回收器会根据一定的策略来回收软引用指向的对象,以释放内存。但是,它会在释放之前尽量保证软引用指向的对象可以存活一段时间,这样就可以用于实现内存敏感的缓存。

弱引用(Weak Reference):

  • 弱引用比软引用更弱一些,可以通过 WeakReference 类来创建。

  • 垃圾回收器会在下一次垃圾回收时,无论内存是否充足,都会回收弱引用指向的对象。弱引用通常用于实现缓存或者监视对象是否已经被回收。

虚引用(Phantom Reference):

  • 虚引用是最弱的引用类型,创建时需要传入一个引用队列(ReferenceQueue)。

  • 虚引用无法通过 get() 方法获取引用的对象,主要用于跟踪对象被垃圾回收器回收的情况。当对象被回收时,虚引用会被放入引用队列中,通过监视引用队列可以知道对象何时被回收。

import java.lang.ref.*;

public class ReferenceExample {
    public static void main(String[] args) {
        // 创建对象
        Object obj = new Object();

        // 强引用
        Object strongRef = obj;

        // 软引用
        SoftReference<Object> softRef = new SoftReference<>(obj);

        // 弱引用
        WeakReference<Object> weakRef = new WeakReference<>(obj);

        // 虚引用
        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
        PhantomReference<Object> phantomRef = new PhantomReference<>(obj, referenceQueue);

        // 打印引用对象
        System.out.println("Strong reference: " + strongRef);
        System.out.println("Soft reference: " + softRef.get());
        System.out.println("Weak reference: " + weakRef.get());
        System.out.println("Phantom reference: " + phantomRef.get());

        // 移除强引用
        obj = null;

        // 手动执行垃圾回收
        System.gc();

        // 检查引用队列中是否有虚引用
        Reference<?> refFromQueue = referenceQueue.poll();
        System.out.println("Reference from queue: " + refFromQueue);
    }
}

##5、何时进行垃圾回收

**内存不足:**当堆内存中的对象达到一定阈值,且无法再分配新对象时(GC日志会有体现),垃圾回收器会启动垃圾回收来释放内存空间,以便给新对象分配内存。

**系统空闲时间:**当系统空闲时间足够长时,垃圾回收器可能会选择在这段时间内进行垃圾回收,以减少对系统性能的影响。

**显式调用:**虽然一般情况下不需要手动触发垃圾回收,但是可以通过调用System.gc()或Runtime.getRuntime().gc()来请求系统执行垃圾回收。但是,这只是一个建议,垃圾回收器是否真正执行垃圾回收取决于具体的实现和条件。

6、STW

STW(Stop-The-World)机制是指在垃圾回收过程中,Java应用程序的所有线程都会被暂停,以便垃圾回收器能够安全地执行其工作。STW机制的主要目的是确保垃圾回收器能够正确地管理内存,并且不会因为并发执行而导致数据不一致或错误。

STW机制通常发生在以下情况下:

Minor GC(新生代垃圾回收): 在新生代进行垃圾回收时,如果存活对象占用的空间超过了新生代的一定比例,就会触发Minor GC。在Minor GC期间,所有工作线程都会暂停,垃圾回收器会扫描新生代中的对象,并将不再使用的对象清除掉。

**Major GC/Full GC(老年代垃圾回收): **在老年代进行垃圾回收时,通常会触发Major GC或者Full GC。在Major GC或Full GC期间,除了新生代的对象,还会对整个堆内存进行回收。这时,所有工作线程都会被暂停,以确保堆内存中的所有对象都被垃圾回收器正确处理。

STW机制的实现对于垃圾回收器的正确执行至关重要,但它也会对应用程序的性能产生影响,特别是在处理大型内存或频繁发生垃圾回收的情况下。因此,优化垃圾回收器的性能以减少STW时间是提高应用程序整体性能的重要方面之一。

问题:为什么桌面程序不使用java编写?

##7、垃圾回收算法

详细可以参考:常见的几种垃圾回收算法

Java虚拟机中的垃圾回收器通常根据其算法和实现方式的不同,可以分为不同类型,例如:

新生代垃圾回收器:针对年轻代(新生代)进行垃圾回收,通常采用复制算法或者标记-复制算法。

老年代垃圾回收器:针对老年代进行垃圾回收,通常采用标记-清除算法或者标记-整理算法。

混合垃圾回收器:结合了新生代和老年代的垃圾回收策略,以实现更好的性能和效果。

标记-清除(Mark-Sweep)算法:

标记-清除算法是最基本的垃圾回收算法之一。它分为两个阶段:标记阶段和清除阶段。在标记阶段,垃圾收集器标记所有活动对象;在清除阶段,垃圾收集器清除所有未标记的对象。这种算法的缺点是会产生内存碎片。

复制(Copying)算法:

复制算法将堆内存划分为两个区域,一部分是活动对象存活的From空间,另一部分是To空间。当From空间满时,垃圾收集器将活动对象复制到To空间,并清除From空间中的所有对象。这种算法减少了内存碎片,但需要额外的空间用于复制对象。

标记-整理(Mark-Compact)算法:
标记-整理算法是标记-清除算法的改进版本。在标记阶段,垃圾收集器标记所有活动对象;在整理阶段,垃圾收集器将活动对象移动到堆的一端,并更新引用。这种算法消除了内存碎片,但需要移动对象,可能会影响性能。

分代(Generational)算法:

分代算法是根据对象的存活周期将堆内存划分为不同的代(Generation),一般分为年轻代(Young Generation)和老年代(Old Generation)。年轻代使用复制算法,老年代使用标记-整理算法。这种算法利用了大部分对象的短暂存活周期,提高了垃圾回收的效率。

增量(Incremental)算法:
增量算法将垃圾回收过程分解为多个阶段,并在每个阶段之间允许应用程序执行一些操作。这样可以减少垃圾回收造成的停顿时间,提高应用程序的响应性。增量算法通常与标记-清除或标记-整理算法结合使用。

并发(Concurrent)算法:

并发算法允许垃圾回收过程与应用程序同时执行,减少了垃圾回收造成的停顿时间。并发算法通常需要牺牲一些性能来换取更低的停顿时间,因为它们需要与应用程序竞争CPU和内存资源。

##8、垃圾回收器

Serial收集器

  • Serial收集器是一种单线程的垃圾回收器,主要用于新生代。它使用复制算法进行垃圾回收,简单高效,适合于单线程的应用和客户端应用。

Parallel收集器

  • Parallel收集器也称为多线程收集器,主要用于新生代和老年代。它使用复制算法和标记-整理算法进行垃圾回收,利用多个线程并行处理垃圾回收任务,提高了垃圾回收的吞吐量。

CMS收集器(Concurrent Mark-Sweep):

  • CMS收集器是一种并发垃圾回收器,主要用于老年代。它使用标记-清除算法进行垃圾回收,在标记和清除阶段与应用程序并发执行,减少了停顿时间,适合于对停顿时间要求较高的应用。

G1收集器(Garbage-First):

  • G1收集器是一种面向服务端应用的垃圾回收器,主要用于新生代和老年代。它使用分代算法和标记-整理算法进行垃圾回收,将堆内存划分为多个区域,并根据垃圾回收的目标选择回收区域,适合于对吞吐量和停顿时间都有要求的应用。

ZGC收集器(Z Garbage Collector):

  • ZGC收集器是一种低延迟垃圾回收器,主要用于大内存应用。它使用并发算法和分代算法进行垃圾回收,与应用程序并发执行,减少了停顿时间,适合于对停顿时间要求非常高的应用。

Shenandoah收集器

  • Shenandoah收集器是一种低延迟垃圾回收器,主要用于大内存应用。它使用并发算法进行垃圾回收,在标记和清理阶段与应用程序并发执行,减少了停顿时间,适合于对停顿时间要求非常高的应用。

##9、内存泄漏的场景

  • 静态集合类
public class StaticCollectionLeak {
    private static List<Object> list = new ArrayList<>();

    public static void main(String[] args) {
        while (true) {
            Object obj = new Object();
            list.add(obj);
        }
    }
}

  • 单例模式
public class SingletonLeak {
    private static SingletonLeak instance;

    private SingletonLeak() {}

    public static SingletonLeak getInstance() {
        if (instance == null) {
            instance = new SingletonLeak();
        }
        return instance;
    }
}

  • 内部类持有外部类
public class OuterClass {
    private class InnerClass {
        // Inner class holding reference to outer class
        private OuterClass outer;

        public InnerClass(OuterClass outer) {
            this.outer = outer;
        }
    }
}

  • 各种连接
public class ConnectionLeak {
    public static void main(String[] args) {
        while (true) {
            // Open database connection but don't close it
            Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "username", "password");
            // Perform database operations...
        }
    }
}


  • 变量不合理的作用域
public class ScopeLeak {
    public static void main(String[] args) {
        List<Object> list;
        while (true) {
            list = new ArrayList<>(); // Define list within the loop
            // Populate and use the list...
        }
    }
}


  • 缓存泄露
public class CacheLeak {
    private static Map<String, Object> cache = new HashMap<>();

    public static void main(String[] args) {
        while (true) {
            // Populate cache but never remove entries
            String key = "key";
            Object value = new Object();
            cache.put(key, value);
        }
    }
}

  • 监听和回调
public class ListenerLeak {
    public interface Listener {
        void onEvent();
    }

    public static class MyClass {
        private Listener listener;

        public void setListener(Listener listener) {
            this.listener = listener;
        }
    }

    public static void main(String[] args) {
        MyClass obj = new MyClass();
        obj.setListener(() -> {
            // Do something
        });
        // obj is no longer needed but listener is still holding a reference
    }
}

  • 队列长度过长
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS, new LinkedBlockingDeque<>(10000));

四、性能监控工具

**JProfiler:**JProfiler是一个功能强大的Java应用性能分析工具,用于监视、分析和优化Java应用程序的性能。它提供了丰富的功能和可视化工具,帮助开发人员识别和解决性能瓶颈、内存泄漏等问题。

常用功能:本地分析、dump文件分析

五、相关技术

TLAB技术、栈上分配、逃逸分析和标量替换

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值