IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 学习笔记day07(GC回收机制+JVM优化) -> 正文阅读

[Java知识库]学习笔记day07(GC回收机制+JVM优化)

一.java内存

1)私有内存区:伴随线程的产生而产生,一旦线程终止,私有内存区也会自动消除

2) 程序计数器 :指示当前程序执行到了哪一行,执行java方法时记录正在执行的虚拟机字节码指令地址

3)虚拟机栈:用于执行java方法,栈帧存储局部变量表

4)Java堆:java虚拟机管理的内存中最大的一块,所有线程共享,几乎所有的对象示例和数组都在这里分配内存,GC主要就是在Java堆中进行的。 堆内存又分为:新生代(新生代又分为Eden80%,Survivor20%)和老年代(Old),并且一般新生代的空间比老年代大。

方法区:只有一个方法区共享。实际也是堆,只是用于存储类,常量相关的信息,来存放程序中永远不变或唯一的内容(类信息【Class对象】,静态变量,字符串常量等)。但是已经被最新的 JVM 取消了。现在,被加载的类作为元数据加载到底层操作系统的本地内存区

二、垃圾回收机制

1、GC的主要任务

分配内存,确保被引用对象的内存不被错误的回收

回收不再被引用的对象的内存空间

2、如何判断那些内存需要回收

垃圾收集器再对堆内存进行回收前,确定对象中那些是存活或者死亡

1)引用计数算法

每当一个地方引用它时,计数器+1;引用失效时,计数器-1;计数值=0——不可能再被引用。

缺点是很难解决对象之间相互矛盾循环引用的问题。

2)可达性分析算法

树图,把一系列“GC Roots”作为起始点,从节点向下搜索,路径称为引用链,当一个对象到GC Roots没有任何引用链相连,即不可达时,则证明此对象时不可用的。

3.如何回收 垃圾收集算法

1)标记清除算法

将需要回收的内存进行标记后清除

缺点是会导致效率和空间问题,产生大量不连续的内存碎片

2)复制算法

为了解决效率问题,将可用内存划分为两块,每次只使用其中一块,当这块内存用完,将存活的对象复制到另一块上去,然后将使用过的内存空间清理

缺点是内存缩小了一半

3)标记整理算法

其实就是标记清除算法,只不过在清除的时候让存货的对象都向一段移动

4)分代收集算法

根据对象存活周期的不同将内存划分为几块。一般是把堆划分为新生代和老年代,这样就可以根据各个年代的特点采用最适合的收集算法。

在新生代中,每次垃圾收集都发现有大批对象死去,就采用复制算法,只需要付出少量存活对象的复制成本 就可以完成收集 新生代又分为Eden80%,Survivor20%

而在老年代中,因为对象存活率高,存活的对象比较多,没有额外空间对他进行分配,因此使用标记清理 或者标记整理算法来进行回收

4、垃圾回收流程

1)新建的对象,大部分存储在Eden中

2)当Eden内存不够,就进行Minor GC释放掉不活跃对象;然后将部分活跃对象复制到Survivor中(如Survivor1),同时清空Eden区

3)当Eden区再次满了,将Survivor1中不能清空的对象存放到另一个Survivor中(如Survivor2),同时将Eden区中的不能清空的对象,复制到Survivor1,同时清空Eden区

4)重复多次(默认15次):Survivor中没有被清理的对象就会复制到老年区(Old)

5) 当Old达到一定比例,则会触发Major GC释放老年代

6) 当Old区满了,则触发一个一次完整的垃圾回收(Full GC)

7) 如果内存还是不够,JVM会抛出内存不足,发生oom,内存泄漏。

5、编写代码测试

public class TestGC {
    public static void main(String[] args) throws InterruptedException {
        List<Object> list = new ArrayList<Object>();
        while (true){
            int sleep = new Random().nextInt(1000);
            if(System.currentTimeMillis()%2==0){
                list.clear();;
            }else{
                for(int i= 0;i<10000;i++){
                    Properties properties = new Properties();
                    properties.put("key_"+i,"value_"+System.currentTimeMillis()+i);
                    list.add(properties);
                }
            }
            Thread.sleep(sleep);
        }
    }
}

JVM参数:-XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:+PrintGCDetails -Xms256m

运行观察日志? 日志分析工具:http://gceasy.io/

[GC pause (G1 Evacuation Pause) (young), 0.0056544 secs]
? ?[Parallel Time: 4.2 ms, GC Workers: 8]
? ? ? [GC Worker Start (ms): Min: 128472.4, Avg: 128472.4, Max: 128472.5, Diff: 0.2]
? ? ? [Ext Root Scanning (ms): Min: 0.0, Avg: 0.1, Max: 0.2, Diff: 0.2, Sum: 0.8]
? ? ? [Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.1, Diff: 0.1, Sum: 0.2]
? ? ? ? ?[Processed Buffers: Min: 0, Avg: 0.9, Max: 2, Diff: 2, Sum: 7]
? ? ? [Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1]
? ? ? [Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
? ? ? [Object Copy (ms): Min: 3.4, Avg: 3.5, Max: 3.6, Diff: 0.1, Sum: 28.0]
? ? ? [Termination (ms): Min: 0.0, Avg: 0.1, Max: 0.2, Diff: 0.2, Sum: 0.8]
? ? ? ? ?[Termination Attempts: Min: 80, Avg: 116.8, Max: 140, Diff: 60, Sum: 934]
? ? ? [GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.1, Diff: 0.1, Sum: 0.2]
? ? ? [GC Worker Total (ms): Min: 3.6, Avg: 3.8, Max: 3.8, Diff: 0.2, Sum: 30.1]
? ? ? [GC Worker End (ms): Min: 128476.2, Avg: 128476.2, Max: 128476.3, Diff: 0.1]
? ?[Code Root Fixup: 0.0 ms]
? ?[Code Root Purge: 0.0 ms]
? ?[Clear CT: 0.6 ms]
? ?[Other: 0.8 ms]
? ? ? [Choose CSet: 0.0 ms]
? ? ? [Ref Proc: 0.4 ms]
? ? ? [Ref Enq: 0.0 ms]
? ? ? [Redirty Cards: 0.3 ms]
? ? ? [Humongous Register: 0.0 ms]
? ? ? [Humongous Reclaim: 0.0 ms]
? ? ? [Free CSet: 0.0 ms]
? ?[Eden: 150.0M(150.0M)->0.0B(144.0M) Survivors: 3072.0K->9216.0K Heap: 169.0M(256.0M)->25.1M(256.0M)]
?[Times: user=0.00 sys=0.00, real=0.01 secs]?

Process finished with exit code -1
?

  • Eden: 12.0M(12.0M)->0.0B(14.0M)?— 表示伊甸园(Eden)空间是 12mb,并且 12mb 空间全部被占用。在 GC 发生之后,年轻代(young generation)空间下降到0,伊甸园的空间增长到 14mb,但是没有提交。因为要求,额外的空间被添加给伊甸园。

  • Survivors: 0.0B->2048.0K?- 表示在 GC 发生之前,幸存者空间(Survivor space)是 0 个字节,但是在 GC 发生之后,幸存者空间增长到 2048 kb,它表明对象从年轻代(Young Generation)提升到幸存者空间(Survivor space)。

  • Heap: 12.6M(252.0M)->7848.3K(252.0M)?– 表示堆的大小是 252mb,被占用 12.6mb,GC 发生之后,堆占用率将至 7848.3kb(即5mb (12.6mb – 7848.3kb)的对象被垃圾回收了),堆的大小仍然是 252mb。

6、可视化GC日志分析工具

‐XX:+PrintGC 输出GC日志
‐XX:+PrintGCDetails 输出GC的详细日志
‐XX:+PrintGCTimeStamps 输出GC的时间戳(以基准时间的形式)
‐XX:+PrintGCDateStamps 输出GC的时间戳(以日期的形式,如 2013‐05‐
04T21:53:59.234+0800)
‐XX:+PrintHeapAtGC 在进行GC的前后打印出堆的信息
‐Xloggc:../logs/gc.log 日志文件的输出路径

测试设置参数
-XX:+UseG1GC
-XX:MaxGCPauseMillis=100
-Xms256m
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
‐XX:+PrintGCDateStamps
‐XX:+PrintHeapAtGC
‐Xloggc:E://Study//test//jvm//gc.log

上传日志?得到报告

?

?

三、JVM优化

1、为什么要对JVM做优化?

解决应用卡住? 日志不输出?程序没有反应? ?服务器CPU负载突然升高? 分配线程数量?等问题

适当的调整jvm的内存大小,可以充分利用服务器资源,让程序跑的更快

2、常用命令

java -version ?查看jvm版本
java -x ? 查看非标准参数
-xx参数也是非标准参数 主要用于jvm的调优和debug操作

如:-XX:+DisableExplicitGC 表示禁用手动调用gc操作,也就是说调用
System.gc()无效

-Xms与-Xmx分别是设置jvm的堆内存的初始大小和最大大小。
-Xmx2048m:等价于-XX:MaxHeapSize,设置JVM最大堆内存为2048M。
-Xms512m:等价于-XX:InitialHeapSize,设置JVM初始堆内存为512M。
适当的调整jvm的内存大小,可以充分利用服务器资源,让程序跑的更快

java ‐Xms512m ‐Xmx2048m TestJVM

3、通过jstat命令查看堆内存使用情况

E:\Study\test>jps
7264
20152 Jps

E:\Study\test>jstat -class 7264
Loaded  Bytes  Unloaded  Bytes     Time
69478 143929.7     2445  2738.5     182.31

Loaded:加载class的数量
Bytes:所占用空间大小
Unloaded:未加载数量
Bytes:未加载占用空间
Time:时间

4、垃圾回收统计 jstat -gc 7264?

S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT
29184.0 29184.0 7919.7  0.0   233664.0 131073.0  583844.0   196526.6  439784.0 418569.4 60388.0 52855.2    344    2.466   7      8.294   10.760
S0C:第一个Survivor区的大小(KB)
S1C:第二个Survivor区的大小(KB)
S0U:第一个Survivor区的使用大小(KB)
S1U:第二个Survivor区的使用大小(KB)
EC:Eden区的大小(KB)
EU:Eden区的使用大小(KB)
OC:Old区大小(KB)
OU:Old使用大小(KB)
MC:方法区大小(KB)
MU:方法区使用大小(KB)
CCSC:压缩类空间大小(KB)
CCSU:压缩类空间使用大小(KB)
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间

?5、.jmap的使用以及内存溢出分析

1)查看内存使用情况   jmap -heap 7264
https://blog.csdn.net/coding__coder/article/details/107570307
2)jmap -hosto 7264 | more 查看所有对象 包括活跃以及非活跃的
3)将内存使用情况dump到文件中 jmap -dump:format=b,file=E:\Study\test\test.dat 7264
C:\Users\21016> jmap -dump:format=b,file=E:\Study\test\test.dat 7264
Dumping heap to E:\Study\test\test.dat ...
Heap dump file created
4)通过jhat对dump文件进行分析 
我们将jvm的内存dump到文件中 这个文件时一个二进制的文件 不方便查看 这时可以借助jhat工具进行查看
C:\Users\21016>jhat -port 9999 E:\Study\test\test.dat
Reading from E:\Study\test\test.dat...
Dump file created Thu Sep 24 20:19:42 CST 2020
Snapshot read, resolving...
Resolving 4984647 objects...
Chasing references, expect 996 dots.
打开浏览器访问:

6、通过MAT工具对dump文件进行分析


1.MAT(Memory Analyzer Tool),一个基于Eclipse的内存分析工具,是一个快速、功能丰
富的JAVA heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗。使用内存分析
工具从众多的对象中进行分析,快速的计算出在内存中对象的占用大小,看看是谁阻止
了垃圾收集器的回收工作,并可以通过报表直观的查看到可能造成这种结果的对象。

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

7、内存溢出的定位与分析

内存溢出在实际的生产环境中经常会遇到,比如,不断的将数据写入到一个集合中,出
现了死循环,读取超大的文件等等,都可能会造成内存溢出。
如果出现了内存溢出,首先我们需要定位到发生内存溢出的环节,并且进行分析,是正
常还是非正常情况,如果是正常的需求,就应该考虑加大内存的设置,如果是非正常需
求,那么就要对代码进行修改,修复这个bug。
首先,我们得先学会如何定位问题,然后再进行分析。如何定位问题呢,我们需要借助
于jmap与MAT工具进行定位分析。
?

public class TestJvmOutOfMemory {
    public static void main(String[] args) {
        List<Object> list = new ArrayList<>();
        for (int i = 0; i < 10000000; i++) {
            String str = "";
            for (int j = 0; j < 1000; j++) {
                str += UUID.randomUUID().toString();
            }
            list.add(str);
        }
        System.out.println("ok");
    }
}

设置参数:‐Xms8m ‐Xmx8m ‐XX:+HeapDumpOnOutOfMemoryError

java.lang.OutOfMemoryError: Java heap space

Dumping heap to java_pid4772.hprof ...

Heap dump file created [9245221 bytes in 0.038 secs]

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

????at java.util.Arrays.copyOf(Arrays.java:3332)

????at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)

????at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448)

????at java.lang.StringBuilder.append(StringBuilder.java:136)

????at cn.mrhan.frames.jvm.TestJvmOutOfMemory.main(TestJvmOutOfMemory.java:13)

Process finished with exit code 1

?将生成的文件导入mat查看

?可以看到,有91.03%的内存由Object[]数组占有,所以比较可疑。
分析:这个可疑是正确的,因为已经有超过90%的内存都被它占有,这是非常有可能出
现内存溢出的。
查看详情:

可以看到集合中存储了大量的uuid字符串

8、jstack的使用

1)有些时候我们需要查看jvm中的线程执行情况 比如 发现服务器的CPU的负载突然增高了 出现了死锁 ?死循环等 该如何分析
由于程序是正常运行的,没有任何的输出,从日志方面也看不出什么问题,所以就需要
看下jvm的内部线程的执行情况,然后再进行分析查找出原因。
这个时候,就需要借助于jstack命令了,jstack的作用是将正在运行的jvm的线程情况进
行快照,并且打印出来:

用法:jstack <pid>

2)java中线程状态

1)初始状态(NEW):创建一个Thread对象 但还未调用start()启动线程时 线程处于初始状态
2)运行状态(RUNNABLE) ?在Java中 运行态包括就绪态和运行态
就绪态 ?改状态下的线程已经获得执行所需的所有资源 只要CPU分配执行权就能运行
? ? ? ? ? ? 所有就绪态的线程存放在就绪队列中
运行态 ?获得CPU执行权 ,正在执行的线程
? ? ? ? ? ? 由于一个CPU同一时刻只能执行一条线程 因此每个CPU每个时刻只有一条运行态的线程
3)阻塞态(BLOCKED):当一个正在执行的线程请求某一资源失败时 就会进入阻塞态
在java中 阻塞态专指请求锁失败时进入的状态
由一个阻塞队列存放所有阻塞态的线程

4)等待态(WAITING)
当前线程中调用wait、join、park函数时,当前线程就会进入等待态。
也有一个等待队列存放所有等待态的线程。
线程处于等待态表示它需要等待其他线程的指示才能继续运行。
进入等待态的线程会释放CPU执行权,并释放资源(如:锁)
5)超时等待态(TIMED_WAITING)
当运行中的线程调用sleep(time)、wait、join、parkNanos、parkUntil时,就
会进入该状态;
它和等待态一样,并不是因为请求不到资源,而是主动进入,并且进入后需要其
他线程唤醒;
进入该状态后释放CPU执行权 和 占有的资源。
与等待态的区别:到了超时时间后自动进入阻塞队列,开始竞争锁。
6)终止态(TERMINATED)
线程执行结束后的状态。

3)死锁问题

如果在生产环境发生了死锁 我们将看到的是部署的程序没有任何反应,这个时候我们可以借助jstack进行分析

1.构造死锁

编写代码?启动2个线程 Threaf2拿到obj1锁? 准备去拿obj2锁? obj2已经被Thread2锁定? 所以发生了死锁

public class TestDeadLock {

    private static Object obj1 = new Object();
    private static Object obj2 = new Object();

    public static void main(String[] args) {
        new Thread(new Thread1()).start();
        new Thread(new Thread2()).start();
    }

    private static class Thread1 implements Runnable {
        @Override
        public void run() {
            synchronized (obj1) {
                System.out.println("Thread1 拿到了 obj1 的锁!");
                try {// 停顿2秒的意义在于,让Thread2线程拿到obj2的锁
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (obj2) {
                    System.out.println("Thread1 拿到了 obj2 的锁!");
                }
            }
        }
    }


    private static class Thread2 implements Runnable {
        @Override
        public void run() {
            synchronized (obj2) {
                System.out.println("Thread2 拿到了 obj2 的锁!");
                try {
                    // 停顿2秒的意义在于,让Thread1线程拿到obj1的锁
                    Thread.sleep(2000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                synchronized (obj1) {
                    System.out.println("Thread2 拿到了obj1的锁!");
                }
            }
        }
    }

}

2.测试:此时出现了死锁的情况

?3.使用jstack进行分析

C:\Users\21016>jps
13252 RemoteMavenServer
25700 Launcher
5700
14792 TestDeadLock
1756 Jps
22972 KotlinCompileDaemon

Found one Java-level deadlock:

=============================

"Thread-1":

??waiting to lock monitor 0x000000000328d088 (object 0x000000076b4de0a0, a java.lang.Object),

??which is held by "Thread-0"

"Thread-0":

??waiting to lock monitor 0x000000000328bbe8 (object 0x000000076b4de0b0, a java.lang.Object),

??which is held by "Thread-1"

Java stack information for the threads listed above:

===================================================

"Thread-1":

????????at TestDeadLock$Thread2.run(TestDeadLock.java:41)

????????- waiting to lock <0x000000076b4de0a0> (a java.lang.Object)

????????- locked <0x000000076b4de0b0> (a java.lang.Object)

????????at java.lang.Thread.run(Unknown Source)

"Thread-0":

????????at TestDeadLock$Thread1.run(TestDeadLock.java:22)

????????- waiting to lock <0x000000076b4de0b0> (a java.lang.Object)

????????- locked <0x000000076b4de0a0> (a java.lang.Object)

????????at java.lang.Thread.run(Unknown Source)

Found 1 deadlock.

可以看到 Thread-1 和Thread-2互相等待对方的锁? 造成了死锁

4、 VisualVM工具的使用

VisualVM 能够监控线程 内存情况 ?查看方法的CPU时间和内存中的对象 已被GC的对象 反向查看分配的堆栈

内存信息
线程信息
Dump堆(本地进程)
Dump线程(本地进程)
打开堆Dump。堆Dump可以用jmap来生成。
打开线程Dump
生成应用快照(包含内存信息、线程信息等等)
性能分析。
CPU分析 ?各个方法调用时间 哪些方法耗时多 ?内存分析等

启动:jdk 安装目录bin目录下

?2)查看CPU、内存、类、线程运行信息

?

查看线程详情

?

?

?

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-08-06 09:29:41  更:2021-08-06 09:30:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/11 16:29:49-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码