JVM性能调优3_垃圾收集器与内存分配策略__享学课堂

6 篇文章 0 订阅
本文深入探讨JVM的Stop The World现象,内存分配策略,包括对象优先在Eden分配和大对象直接进入老年代。讲解了JVM调优中涉及的新生代配置,如-XX:NewSize、MaxTenuringThreshold等参数,并介绍了JDK提供的多种诊断工具,如jps、jstat、jmap等,用于分析和解决内存泄漏、内存溢出等问题。
摘要由CSDN通过智能技术生成

Stop The World现象

GC收集器和我们GC调优的目标就是尽可能的减少STW的时间和次数。

内存分配与回收策略

对象优先在Eden分配,如果说Eden内存空间不足,就会发生Minor GC
大对象直接进入老年代,大对象:需要大量连续内存空间的Java对象,比如很长的字符串和大型数组;容易出现的问题:1、导致内存有空间,还是需要提前进行垃圾回收获取连续空间来放他们,2、会进行大量的内存复制。
-XX:PretenureSizeThreshold 参数 ,大于这个数量直接在老年代分配;缺省为0 ,表示绝不会直接分配在老年代。
长期存活的对象将进入老年代,默认15岁,-XX:MaxTenuringThreshold调整
动态对象年龄判定,为了能更好地适应不同程序的内存状况,虚拟机并不是永远地要求对象的年龄必须达到了MaxTenuringThreshold才能晋升老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄
空间分配担保:新生代中有大量的对象存活,survivor空间不够,当出现大量对象在MinorGC后仍然存活的情况(最极端的情况就是内存回收后新生代中所有对象都存活),就需要老年代进行分配担保,把Survivor无法容纳的对象直接进入老年代.只要老年代的连续空间大于新生代对象的总大小或者历次晋升的平均大小,就进行Minor GC,否则FullGC。

新生代配置

新生代大小配置参数的优先级:
高:-XX:NewSize/MaxNewSize
中间 -Xmn (NewSize= MaxNewSize)
低:-XX:NewRatio 表示比例,例如=2,表示 新生代:老年代 = 1:2

-XX:SurvivorRatio 表示Eden和Survivor的比值,
缺省为8 表示 Eden:FromSurvivor:ToSurvivor= 8:1:1
在这里插入图片描述
在这里插入图片描述
同样的代码情况下:
-Xms20M -Xmx20M -XX:+PrintGCDetails –Xmn2m -XX:SurvivorRatio=2
没有垃圾回收
说明所有的对象全部分配到了老年代。

如果参数为:
-Xms20M -Xmx20M -XX:+PrintGCDetails -Xmn7m -XX:SurvivorRatio=2

在这里插入图片描述
发生了垃圾回收;
新生代存了部分数组,老年代也保存了部分数组,发生了晋升现象(动态年龄判定)

-Xms20M -Xmx20M -XX:+PrintGCDetails -Xmn15m -XX:SurvivorRatio=8
在这里插入图片描述
新生代可以放下所有的数组
老年代没放

-Xms20M -Xmx20M -XX:+PrintGCDetails -XX:NewRatio=2
NewRatio新生代(Eden + 2*S)与老年代(不包括永久区)的比值;
这里新生代6.6,;老年代13.4
在这里插入图片描述
老年代放了9个,新生代放了1个;

也就是新生代放第10个的时候,不够了,发生了空间担保,将原来的9个同龄的放到老年代中。

发生了垃圾回收
出现了空间分配担保,而且发生了FullGC

内存泄漏和内存溢出辨析

内存溢出:实实在在的内存空间不足导致;
内存泄漏:该释放的对象没有释放,多见于自己使用容器保存元素的情况下。

示例代码:

public class Stack {
   
   public  Object[] elements;
   private int size = 0;//指示器,指示当前栈顶的位置

    private static final int Cap = 16;

    public Stack() {
       elements = new Object[Cap];
    }

    //入栈
    public void push(Object e){
       elements[size] = e;
       size++;
    }

    //出栈
    public Object pop(){
       size = size-1;
       Object o = elements[size];
       elements[size] = null;  //clear to let GC do its work
        return o;
    }
}

public class UseStack {

    public static void main(String[] args) {
        Stack stack = new Stack();
        Object o = new Object();
        System.out.println("o="+o);
        stack.push(o);
        Object o1 =  stack.pop();
        System.out.println("o1="+o1);
        
        System.out.println(stack.elements[0]);
        
    }
}

JDK为我们提供的工具

在这里插入图片描述

jps

列出当前机器上正在运行的虚拟机进程
-p :仅仅显示VM 标示,不显示jar,class, main参数等信息.
-m:输出主函数传入的参数. 下的hello 就是在执行程序时从命令行输入的参数
-l: 输出应用程序主类完整package名称或jar完整名称.
-v: 列出jvm参数, -Xms20m -Xmx50m是启动程序指定的jvm参数

在这里插入图片描述
在这里插入图片描述
Jinfo可以动态的给程序设置参数(只有现实manageable的可以修改)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

jstat

是用于监视虚拟机各种运行状态信息的命令行工具。它可以显示本地或者远程虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据,在没有GUI图形界面,只提供了纯文本控制台环境的服务器上,它将是运行期定位虚拟机性能问题的首选工具。
假设需要每250毫秒查询一次进程2764垃圾收集状况,一共查询20次,那命令应当是:jstat-gc 2764 250 20
常用参数:
-class (类加载器)
-compiler (JIT)
-gc (GC堆状态)
-gccapacity (各区大小)
-gccause (最近一次GC统计和原因)
-gcnew (新区统计)
-gcnewcapacity (新区大小)
-gcold (老区统计)
-gcoldcapacity (老区大小)
-gcpermcapacity (永久区大小)
-gcutil (GC统计汇总)
-printcompilation (HotSpot编译统计)

jinfo

查看和修改虚拟机的参数
jinfo –sysprops 可以查看由System.getProperties()取得的参数
jinfo –flag 未被显式指定的参数的系统默认值
jinfo –flags(注意s)显示虚拟机的参数
jinfo –flag +[参数] 可以增加参数,但是仅限于由java -XX:+PrintFlagsFinal –version查询出来且为manageable的参数

在这里插入图片描述
jinfo –flag -[参数] 可以去除参数
Thread.getAllStackTraces();

Jmap

用于生成堆转储快照(一般称为heapdump或dump文件)。jmap的作用并不仅仅是为了获取dump文件,它还可以查询finalize执行队列、Java堆和永久代的详细信息,如空间使用率、当前用的是哪种收集器等。和jinfo命令一样,jmap有不少功能在Windows平台下都是受限的,除了生成dump文件的-dump选项和用于查看每个类的实例、空间占用统计的-histo选项在所有操作系统都提供之外,其余选项都只能在Linux/Solaris下使用。
jmap -dump:live,format=b,file=heap.bin
Sun JDK提供jhat(JVM Heap Analysis Tool)命令与jmap搭配使用,来分析jmap生成的堆转储快照。
在这里插入图片描述
在这里插入图片描述

jhat

jhat dump文件名
后屏幕显示“Server is ready.”的提示后,用户在浏览器中键入http://localhost:7000/就可以访问详情

Jstack

:显示当前应用的多个线程的栈信息,可以用来查看死锁。

(Stack Trace for Java)命令用于生成虚拟机当前时刻的线程快照。线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等都是导致线程长时间停顿的常见原因。
在代码中可以用java.lang.Thread类的getAllStackTraces()方法用于获取虚拟机中所有线程的StackTraceElement对象。使用这个方法可以通过简单的几行代码就完成jstack的大部分功能,在实际项目中不妨调用这个方法做个管理员页面,可以随时使用浏览器来查看线程堆栈。

管理远程进程需要在远程程序的启动参数中增加:
-Djava.rmi.server.hostname=……
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=8888
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false

visualvm

插件中心地址
https://visualvm.github.io

在这里插入图片描述

但是注意版本问题,不同的JDK所带的visualvm是不一样的,下载插件时需要下对应的版本。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

MAT(MemoryAnalyzer.exe)

专门对堆内存分析,查看内存泄漏,内存消耗的情况。
运行程序中,增加参数:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
打开上面的文件
在这里插入图片描述
在这里插入图片描述

浅堆和深堆

浅堆 :(Shallow Heap)是指一个对象所消耗的内存。例如,在32位系统中,一个对象引用会占据4个字节,一个int类型会占据4个字节,long型变量会占据8个字节,每个对象头需要占用8个字节。

深堆 :这个对象被GC回收后,可以真实释放的内存大小,也就是只能通过对象被直接或间接访问到的所有对象的集合。通俗地说,就是指仅被对象所持有的对象的集合。深堆是指对象的保留集中所有的对象的浅堆大小之和。
举例:对象A引用了C和D,对象B引用了C和E。那么对象A的浅堆大小只是A本身,不含C和D,而A的实际大小为A、C、D三者之和。而A的深堆大小为A与D之和,由于对象C还可以通过对象B访问到,因此不在对象A的深堆范围内。
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值