专栏首页个人开发后浪,谈谈你对jvm性能调优的理解

后浪,谈谈你对jvm性能调优的理解

在我们日常的研发工作中, 经常会遇到系统的性能问题,这时我们必须进行系统的性能调优。系统调优分好多种,比如架构和代码优化、jvm调优、操作系统调优、数据库调优、tomcat调优、网络调优等。架构和代码优化是效率最高的调优手段,但是并不能解决所有的性能问题。今天我们要回顾的是一个老生常谈的话题,jvm调优。

本文主要包括以下内容

  • Java内存模型回顾
  • 什么时候需要JVM调优
  • 常见的OOM异常及案例
  • JVM自带监控工具
  • JVM常用调优参数
  • JVM第三方监控工具
  • 调优案例

Java内存模型回顾

首先,我们以HotSpot回顾一下JVM的内存模型,见下图:

HotSpot内存模型分为3个部分:

类加载器

类加载器用于加载java编译后的.class文件,提取其中的类信息以某种数据结构存放在方法区。

运行时数据区

线程栈和本地方法栈用于存放线程运行时方法调用等相关信息,程序计数器记录字节码指令在主内存中的地址,这3个模块都是线程私有的。

堆中存放程序运行时创建的对象。

对于jvm规范中的方法区,java8以前,HotSpot对方法区的实现是在永久代。从java7开始,HotSpot开始移除永久代,符号引用迁移到native heap,字面量和类静态变量移动到java堆。Java8中HotSpot彻底废弃了永久代,用元空间来取代永久代实现jvm规范中的方法区。元空间用来专门存储类的元数据,并且内存分配在本地内存,不占用jvm内存。

堆内存的分布如下:

G1圾收集器的堆空间分配策略如下:

后来出现的ZGC内存分配更加动态和灵活。本文以Java8为例,不讨论G1和ZGC

顺便回顾一下常用的垃圾收集算法:

a. 清除算法:会造成内存碎片,内存分配效率低

b. 压缩算法:性能开销大

c. 复制算法:堆使用效率较低

常用垃圾收集器:

新生代:Serial,Parallel Scavenge(更加注重吞吐量,不能和CMS一起使用) 和 Parallel New,都采用标记-复制算法

老年代:Serial Old(标记-压缩算法) 和 Parallel Old(标记-压缩算法),以及 CMS(标记-清除算法,支持并发),java9中被G1取代

执行引擎

HotSpot解释执行器对加载的字节码会逐条解释成机器码进行执行,对于反复执行的热点代码,JIT Compiler会把字节码编译成机器代码后再执行。

垃圾收集器则是对死亡对象占用的堆内存空间进行回收。

在上面的JVM内存模型架构图中,紫色的3个区域是我们调优时的关注点。Heap是存放对象数据的区域,这个区域由Garbage Collector进行管理,Garbage Collector在JVM启动时可以指定。JVM调优一般都围绕着修改Heap的大小和选择最合适的Garbage Collector。而JIT Compiler虽然也会对应用的性能有大的影响,但是新版本的JVM是不需要进行优化的。

什么时候需要JVM调优

从表象来看,当应用的响应慢或者已经不能提供服务了,或应用吞吐量小,占用内存空间过大,我们就需要对应用进行调优了。这些表象一般伴随着频繁的垃圾回收,或者OOM。

JVM调优的指标一般有3个

应用占用的内存

主要是分配给jvm的堆内存,由启动jvm时-Xms和-Xmx参数指定,分别是jvm启动时分配的内存和运行时可以分配的最大内存。

吞吐量

比如每秒钟处理的事务数量,每小时完成的跑批任务数,每小时完请求数据库成功的数量。

响应延迟

从应用收到请求到返回响应所耗费的时间,或者浏览器发出请求到页面渲染的时间。

常见的OOM异常及复现方法

OOM是我们程序员最不想看到的异常,但是时常发生在我们的工作中。在jvm没有足够内存为新创建的对象分配空间,并且没有足够内存为垃圾收集器使用时就会触发,java应用就会触发OOM。当然,linux本身也有OOM killer机制,当内核监控到进程占用空间过大时,尤其是内存瞬间增大时,为了防止耗尽内存,会触发OOM杀死进程。Java中常见的OOM如下:

java.lang.OutOfMemoryError: Java heap space

这个异常的原因无非2个,内存泄漏和内存溢出。内存溢出的时候,需要调整JVM参数-Xmx配置,调大堆空间,如果是内存泄漏,就需要找出泄漏的代码,这个见后面监控工具讲解。

下面代码是一段典型的内存泄漏的代码,启动时设置-Xmx512m

public class HeapSize {
    public static void main(String[] args){
        List<User> list = new ArrayList<>();
        User user = new User();
        while (true){
            list.add(user);
        }
    }
}

执行后等待一段时间:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.ArrayList.grow(ArrayList.java:261)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
at java.util.ArrayList.add(ArrayList.java:458)
at boot.oom.HeapSize.main(HeapSize.java:18)

java.lang.OutOfMemoryError: GC overhead limit exceeded

这种异常的原因是垃圾收集器GC效率很低,jvm花费超过 98%的 CPU 时间来进行一次 GC,但是回收的内存却少于 2%的堆空间大小,并且GC连续超过5次都这样

public class GcOverrhead {
    public static void main(String[] args){
        Map map = System.getProperties();
        Random r = new Random();
        while (true) {
            map.put(r.nextInt(), "value");
        }
    }
}

上面代码启动时加参数:-Xmx45m -XX:+UseParallelGC -XX:+PrintGCDetails运行一段时间,就会出现以下异常,注意:这个参数只是作者本地的环境,需要根据自己环境相应修改

Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.util.Hashtable.addEntry(Hashtable.java:435)
at java.util.Hashtable.put(Hashtable.java:476)
at boot.oom.HeapSize.main(HeapSize.java:20)

通过增加参数-XX:-UseGCOverheadLimit可以避免这个异常,但其实是自己骗自己,还是需要实际去定位解决问题。

java.lang.OutOfMemoryError: Requested array size exceeds VM limit

这个异常很容易理解,请求分配的数组大小超过jvm限制,出现这种情况的原因有2个:

请求分配的数组太大,导致jvm空间不足

请求的数组大于等于Integer.MAX_INT - 1

如下2段代码:

这段代码直接抛出Requested array size exceeds VM limit

int[] arr = new int[Integer.MAX_VALUE - 1];

这段代码先抛出 Java heap space后再抛出Requested array size exceeds VM limit

for (int i = 3; i >= 0; i--) {
    try {
        int[] arr = new int[Integer.MAX_VALUE-i];
        System.out.format("Successfully initialized an array with %,d elements.\n", Integer.MAX_VALUE-i);
    } catch (Throwable t) {
        t.printStackTrace();
    }
}

结果如下:

java.lang.OutOfMemoryError: Java heap space
at boot.oom.ArraySizeExceeds.main(ArraySizeExceeds.java:12)
java.lang.OutOfMemoryError: Java heap space
at boot.oom.ArraySizeExceeds.main(ArraySizeExceeds.java:12)
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
at boot.oom.ArraySizeExceeds.main(ArraySizeExceeds.java:12)
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
at boot.oom.ArraySizeExceeds.main(ArraySizeExceeds.java:12)

java.lang.OutOfMemoryError: MetaSpace

元空间在前面已经讲过了,这个异常是元空间不足,解决办法是加大元空间大小,配置参数MaxMetaSpaceSize,我们在启动引用时加入参数:

-XX:MaxMetaspaceSize=2m,直接报错:

Error occurred during initialization of VM
OutOfMemoryError: Metaspace

java.lang.OutOfMemoryError: Request size bytes for reason. Out of swap space

这个异常是操作系统的swap空间不够引起的。我们知道jvm分配的最大内存由Xmx等一些参数指定,如果jvm需要的总内存超出了宿主机可以分配的最大的物理内存,就会用到swap space,如果swap space不足,jvm内存分配就会失败,从而抛出这个异常。这个异常的定位比较复杂,有可能是宿主机上面的其他进程耗用内存太多导致。所以我不建议用增加swap space这种粗暴的方式,禁用swap,做好进程的隔离是比较妥当的解决方案。

java.lang.OutOfMemoryError: Unable to create native threads

这个异常也是操作系统级别的。大家知道,java的线程是操作系统级别的,java每申请一个线程,就需要调用操作系统创建一个本地的线程,操作系统创建线程失败,会抛出上面的异常。具体原因有以下几种:

a. 内存空间不够,jvm启动时参数-Xss指定每个线程占用的堆栈大小,如果内存不够,就会创建线程失败

b. 操作系统上ulimit中max user processes参数限制,这个参数指操作系统可以创建的全局线程数量

ulimit -a | grep 'max user processes'命令可以查看,如下图:

ulimit -u可以修改这个参数,比如ulimit -u 10000,则操作系统可以创建10000个线程。

c. 参数sys.kernel.threads-max限制,我们可以通过命令

cat /proc/sys/kernel/threads-max来查看,如下图:

想要修改这个参数,需要在/etc/sysctl.conf文件,加入sys.kernel.threads-max = 10000

d. 参数sys.kernel.pid_max限制,这个参数只是每创建一个线程,都需要分配一个pid,当pid的值大于这个值时,就会创建失败。查看命令:cat /proc/sys/kernel/pid_max

想要修改这个参数,需要在/etc/sysctl.conf文件,加入sys.kernel.pid_max =10000

private static void crateSlowThread(){
        try {
            System.out.println(Thread.currentThread());
            Thread.currentThread().sleep(15000);
        } catch (InterruptedException e)
            e.printStackTrace();
        }
    }

    public static void test1() {
        while (true){
            new Thread(() -> crateSlowThread()).start();
        }
    }

看上面这个代码,在死循环内部不停地创建线程,最后就会复现这个OOM:见下图:

下面这段代码模拟高并发

public static void test() {
        for (int i = 0; i < 20; i ++){
            System.out.println(Thread.currentThread());
            new Thread(() -> crateSlowThread()).start();
        }
    }

    private static void crateSlowThread(){
        try {
            System.out.println(Thread.currentThread());
            Thread.currentThread().sleep(15000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @RequestMapping("/createNativeThreads1")
    public String createNativeThreads1(){
        System.out.println("createNativeThreads test1");
        CreateNativeThreads.test1();
        return "Sucess!";
    }

在jmeter中进行测试:

JVM自带监控工具

JPS列出目标虚拟机上的所有进程

使用示例:jps -mlvV

主要参数-m 打印传递给主类的参数-l 打印模块名以及包名-v 打印jvm启动参数,比如-XX:+HeapDumpOnOutOfMemoryError-V 输出通过标记的文件传递给JVM的参数

jstat主要监控虚拟机的性能数据

使用示例:jstat -gc -h 2 44074 1s 5

基本参数:-t展示从虚拟机运行到现在的性能数据-h n 当n大于0是每隔几行展示行头部信息vmid 展示虚拟机表示interval 展示性能采样数据的间隔时间count 展示性能指标的次数性能参数:Class 类加载器统计信息Compiler 即时编译器统计信息Gc 堆垃圾回收信息Gccapacity 各代的空间信息Gccause 同gcutilGcnew 新生代统计信息Gcnewcapacity 展示新生代空间占用情况Gcold 老年代统计信息Gcoldcapacity 展示老年代空间占用情况Gcmetacapacity:meta space 空间大小信息Gcutil 统计垃圾收集汇总信息Printcompilation:Displays Java HotSpot VM compilation method statistics.

jmap展示指定进程的对象共享内存或堆内存信息

使用示例:

将堆中所有存活对象导出

jmap -dump:live,format=b,file=filename.bin

打印堆中存活对象

jmap -histo:live 44074

主要参数如下:-clstats 展示被加载类的信息-finalizerinfo 展示所有待 finalize 的对象-histo 展示各个类的实例数目以及占用内存,并按照内存使用量从多至少的顺序排列-histo:live 展示堆中的存活对象-dump 导出 Java 虚拟机堆的快照-dump:live 只保存堆中的存活对象

当系统OOM后,如果服务已经挂了,或者监控系统监控到服务关闭后重启,时候通过 jmap命令已经不能导出堆快照了,所以我们需要在启动虚拟机时加入下面2个参数:

-XX:+HeapDumpOnOutOfMemoryError

-XX:HeapDumpPath=\dump

jinfo查看或修改Java 进程的参数

使用示例:

展示参数配置信息jinfo 44074

修改进程参数jinfo -flag +HeapDumpAfterFullGC 44074

主要参数:-flag name 打印参数名是name的参数值-flag [+|-]name 更改bool类型参数值-flag name=value 增加参数对-flags 打印传递给jvm的参数对-sysprops 打印java系统参数对

jstack打印java进程的线程栈信息,已经线程持有的锁

示例:jstack 44074

输出如下:

常用参数:-F -l参数无响应是强制打印快照信息-l 打印有关锁的额外信息比如Locked ownable synchronizers-m Prints a mixed mode stack trace that has both Java and native C/C++ frames.

JVM常用调优参数

堆空间设置: -Xmx4g 进程占用的最大堆空间大小,超出后会OOM -Xms2g 初始化堆空间大小 -Xmn1g 年轻代大小,官方推荐配置为整个堆的3/8 -XX:NewRatio=n 年轻代和老年代空间大小比值 -Xss512k 每个线程占用内存大小 -XX:SurvivorRatio=n:年轻代中Eden区与Survivor区的比值。比如n=4,则Eden和Survivor比值为4:2,survivor占年轻代一半 -XX:MetaspaceSize=512m 元空间大小 -XX:MaxMetaspaceSize=512m 这个参数用于限制Metaspace增长的上限,防止因为某些情况导致Metaspace无限的使用本地内存 -XX:MinMetaspaceFreeRatio=N 当进行过Metaspace GC之后,会计算当前Metaspace的空闲空间比,如果空闲比小于这个参数,那么虚拟机将增长Metaspace的大小。在本机该参数的默认值为40,也就是40%。设置该参数可以控制Metaspace的增长的速度,太小的值会导致Metaspace增长的缓慢,Metaspace的使用逐渐趋于饱和,可能会影响之后类的加载。而太大的值会导致Metaspace增长的过快,浪费内存 -XX:MaxMetasaceFreeRatio=N 当进行过Metaspace GC之后, 会计算当前Metaspace的空闲空间比,如果空闲比大于这个参数,那么虚拟机会释放Metaspace的部分空间。在本机该参数的默认值为70,也就是70%。 -XX:MaxMetaspaceExpansion=N Metaspace增长时的最大幅度 垃圾收集器设置 -XX:+UseSerialGC 设置串行收集器 -XX:+UseParallelGC 设置并行收集器 -XX:+UseParalledlOldGC 设置并行年老代收集器 -XX:+UseConcMarkSweepGC 设置并发收集器 -XX:ParallelGCThreads=n 设置并行收集器收集时使用的线程数 -XX:MaxGCPauseMillis=n 设置并行收集最大暂停时间 -XX:GCTimeRatio=n 设置垃圾回收时间占程序运行时间的百分比,1/(1+n) -XX:+DisableExplicitGC 禁止外部调用System.gc() -XX:MaxTenuringThreshold 年轻代复制多少次才会进入老年代 垃圾回收统计信息 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps 打印每次垃圾回收前,程序未中断的执行时间 -Xloggc:filename 把gc日志存入文件 -XX:+PrintGCApplicationStoppedTime 打印垃圾回收期间程序暂停的时间 -XX:+PrintGCApplicationConcurrentTime 打印每次垃圾回收前,程序未中断的执行时间 -XX:+PrintHeapAtGC 打印GC前后的详细堆栈信息 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/dump

JVM第三方监控工具

eclipse mat

下载地址:https://www.eclipse.org/mat/downloads.php

eclipse mat是分析java应用非常常用的工具,可以集成在eclipse,也可以单独安装。我们还是以之前的一个OOM异常案例来介绍

public static void test(){
        List<User> list = new ArrayList<>();
        User user = new User();
        while (true){
            list.add(user);
        }
    }

启动应用命令:

java -jar -XX:+PrintGC -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./ spring-boot-mybatis-1.0-SNAPSHOT.jar

启动后调用这个方法,程序抛出了OOM,生成了堆转存文件:java_pid46242.hprof,接着我们打开mat工具,导入刚刚的对转存文件,如下图:

MAT 计算对象占据内存方式有2种。第一种是 Shallow heap,统计对象占用内存。第二种是 Retained heap,统计当对象不再被引用时,垃圾回收器所能回收的总内存,包括对象自身所占据的内存,以及仅能够通过该对象引用到的其他对象所占据的内存。上面的饼状图便是基于 Retained heap 的。

从上面的报告中,我们看出有内存泄漏的情况,点进去后就能找到内存泄漏点,如下图:

阿里诊断工具 Arthas

工具地址:

https://alibaba.github.io/arthas/arthas-tutorials?language=cn&id=arthas-basics
https://alibaba.github.io/arthas/arthas-tutorials?language=cn&id=arthas-advanced

使用场景:

找到类所在jar包

找出异常的原因

找到代码没有执行到的原因

线上debug

全局监控系统状态

实时监控 JVM 运行状态

IBM heap anolyzer

这个工具是找出堆内存泄漏的一款图形化工具,界面如下:

官网地址:

https://www.ibm.com/support/pages/ibm-heapanalyzer

这款工具目前IBM已经不再更新,并且官网推荐使用MAT

调优案例

死锁诊断

下面代码是一段经典的死锁案例

public static void test() {
        Object lockA = new Object();
        Object lockB = new Object();

        new Thread(() ->{
            synchronized (lockA){
                try {
                    Thread.sleep(2000);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
                synchronized (lockB){
                    System.out.println("thread 1");
                }
            }
        }).start();

        new Thread(() ->{
            synchronized (lockB){
                synchronized (lockA){
                    System.out.println("thread 2");
                }
            }
        }).start();
    }

在main函数启动后一直不能执行结束,用http方式调用这个方法,发现一直没有返回结果。输入命令:jstack 45309 > deadlock.txt 然后查看生产的文件,能看到BOLOCKED状态的线程,如下图:

堆内存参数设置

我们在java应用启动时加入下面2个参数,就会在日志里面打印详细的垃圾收集信息

-XX:+PrintGC

-XX:+PrintGCDetails

如下是一个Full GC的日志,我们来分析一下

[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(150528K)] [ParOldGen: 243998K->142439K(172032K)] 243998K->142439K(322560K), [Metaspace: 47754K->47754K(1093632K)], 3.6879500 secs] [Times: user=3.91 sys=0.00, real=3.69 secs]

Full GC:表示是一个Full GC垃圾回收,如果不带Full,那就表示是Minor GC

Allocation Failure:本次垃圾回收的原因是因为年轻代没有足够的内存分配给新的对象

[PSYoungGen: 0K->0K(150528K)]:这3个数值分别代表年轻代垃圾收集前占用的堆内存大小,年轻代垃圾收集后占用的堆内存大小,年轻代占用堆内存总大小

[ParOldGen: 243998K->142439K(172032K)]:这3个数值分别代表老年代垃圾收集前占用的堆内存大小,老年代垃圾收集后占用的堆内存大小,老年代占用堆内存总大小

243998K->142439K(322560K):这2个数值分别代表堆内存垃圾收集前使用量,堆内存垃圾收集后使用量,堆空间总大小

[Metaspace: 47754K->47754K(1093632K)]:这3个数值分别代表元空间垃圾收集前占用的内存大小,元空间垃圾收集后占用的内存大小,元空间总大小

3.6879500 secs:本次GC持续的时间

[Times: user=3.91 sys=0.00, real=3.69 secs]:这3个时间表示GC线程消耗的CPU时间,GC过程系统调用和等待花费的时间,应用程序暂停的时间。

堆内存大小设置

上面分析可知,老年代垃圾收集后占用的堆内存大小是142439K=139M

我们根据这个数值来指定堆空间大小,我们的应用中建议-Xms和-Xmx参数设置为一样大小,这样可以减少启动初期的GC次数,同时避免JVM在运行过程中向OS申请内存。这2个参数建议设置为老年代垃圾收集后占用的堆内存大小的3~4倍,本案例中即139M*(3~4),官方建议年轻代设置为堆内存总大小的3/8,所以年轻代大小为-Xmn139M*(3~4) * 3/8

元空间大小设置

上面分析可知,元空间垃圾收集后占用的内存大小是47754K=47M

-XX:MetaspaceSize -XX:MaxMetaspaceSize这2个值建议设置为上面值的1.2~1.5倍,即47M * (1.2~1.5)

综上取最大分析值,启动参数为:

java -jar -Xms556m -Xmx556m -Xmn208m -XX:MetaspaceSize=70m -XX:MaxMetaspaceSize=70m -XX:+PrintGC -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./ spring-boot-mybatis-1.0-SNAPSHOT.jar

垃圾收集时间

对于Minor GC耗费时间跟年轻代空间大小成正比,Minor GC触发频率跟年轻代空间大小成反比。示例如下图:

我们在日志中取样,在10s中时间内Minor GC触发了8次,频率为 次/0.8s,这8次GC的平均时间为:0.05s=50ms

如果我们系统调优指标是40ms,那就需要减小年轻代大小,上面案例中,我们年轻代大小减少20%,208m * 80%

最后,JVM调优是一个永久的话题,本人能力有限,欢迎大家批评指正

参考文章:

https://docs.oracle.com/javase/8/docs/technotes/tools/unix/index.html
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/index.html
https://www.oracle.com/technetwork/tutorials/tutorials-1876574.html#t1s2
https://plumbr.io/outofmemoryerror
http://openjdk.java.net/jeps/122

文中源代码地址:

https://github.com/jinjunzhu/spring-boot-mybatis

本文分享自微信公众号 - jinjunzhu(gh_1f109b82d301),作者:jinjunzhu

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-05-17

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 浅谈kubernete中的flannel网络插件

    容器中的网络,无非就是2个方面,同一台宿主机上面的容器是可以联通的,不同宿主机上的容器相互间也是可以联通的。

    jinjunzhu
  • lombok在java项目中的使用

    @NoArgsConstructor@RequiredArgsConstructor@AllArgsConstructor

    jinjunzhu
  • redis灵魂拷问:聊一聊AOF日志重写

    redis的AOF日志,是redis持久化的一种方式,它是一种write after log,即先执行命令后记录日志。这样的好处是日志不会记录执行失败的命令,同...

    jinjunzhu
  • 一篇年薪60万的JVM性能调优文章

    GC 优化的基本原则是:将不同的 GC 参数应用到两个及以上的服务器上然后比较它们的性能,然后将那些被证明可以提高性能或减少 GC 执行时间的参数应用于最终的工...

    李红
  • 深入理解java虚拟机-第二章:java内存区域与内存泄露异常

    java将内存的管理(主要是回收工作),交由jvm管理,确实很省事,但是一点jvm因内存出现问题,排查起来将会很困难,为了能够成为独当一面的大牛呢,自然要了解...

    用户1134788
  • Java中堆的内存泄漏和内存溢出 及问题解决 参数设置

    1、内存泄漏:严格来说,只有对象不会再被程序用到了,但是GC又不能回收他们的情况,才叫内存泄漏,由于这些对象不能被垃圾收集掉,这样的对象多了,有可能就会OOM。

    一个长不胖的程序Yuan
  • 从内存泄露、内存溢出和堆外内存,JVM优化参数配置参数

    内存泄漏是指程序在申请内存后,无法释放已申请的内存空间,无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而造成内存空间的浪费。

    大数据学习与分享
  • CTF| 攻击取证之内存分析

    在CTF中,内存取证一般指对计算机及相关智能设备运行时的物理内存中存储的临时数据进行获取与分析,提取flag或者与flag相关重要信息。

    漏斗社区
  • 面试杂谈 - 内存泄漏如何排查

    Java的垃圾回收机制给了程序猿便利,我们可以不需要显式释放资源。但想高枕无忧却是不能,OOM像个隐藏在暗处的幽(hua)灵(nong),威胁着可怜、弱小又漂亮...

    Java_老男孩
  • JVM--对象创建

    虚拟机遇到一条new指令时,首先去检查这个指令的参数是否能在常量池中定位到一个符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有,就必...

    SuperHeroes

扫码关注云+社区

领取腾讯云代金券