深入理解JVM(一)

目录

第一章 jvm概述

一 知识点体系

二、 jdk, jre, jvm的关系

三、内存溢出场景模拟

四、jvm性能监控场景模拟

第二章

一、 Java的历史版本

二、1.8的新特性

三、Java 虚拟机的发展

第三章 - java 五大 内存区域

一、jvm内存

二、java 内存区域 - 程序计数器 - 行号

三、java 内存区域 - java虚拟机栈 - 存放栈帧

四、java 内存区域 - - java本地方法栈

五、java 内存区域 - - java堆

六、java 内存区域 - -方法区 

七、java 内存区域 - -方法区 - 常量池

7.1 直接内存 - Nio

第四章  对象的创建

一、给对象分配内存 - 指针碰撞 & 空闲列表

二、线程安全问题 - 线程同步加锁 & 本地线程分配缓冲

第五章、探究对象的结构

一、垃圾回收几个著名的算法

第六章 对象的访问定位方式- 两种

第七章 垃圾回收 GC 

7.1 GC概述

如何判断一个对象是垃圾对象? - 找到一个可回收的对象

如何回收?

何时回收?

7.2 垃圾回收-判断对象是否存活算法-引用计数法详解

7.3 垃圾回收-判断对象是否存活算法-可达性分析法详解

知道了哪些对象需要回收,接下来就要使用垃圾回收算法了,

7.4 标记-清除算法

7.5 复制算法 & 内存浪费的解决方案

7.6 标记 - 整理算法

7.7 分代收集算法

算法讲完了,接下来讲解关于垃圾回收器相关的问题

7.8 垃圾收集器-serial收集器详解.

7.9 垃圾收集器-ParNew收集器详解.

7.10 垃圾收集器-parallel scavenge收集器详解.

7.11 垃圾收集器-CMS收集器详解.

7.12 垃圾收集器-最牛的垃圾收集器-g1收集器详解

第八章、 内存分配

8.1 内存分配概述

8.2 内存分配-Eden区域 - 对象优先分配到Eden区

8.1 内存分配-大对象直接进老年代

8.1 内存分配-长期存活的对象进入老年代

8.1 内存分配 - 空间分配担保

8.1 内存分配-逃逸分析与栈上分配

第九章、虚拟机工具介绍

我们先介绍了jvm,内存结构,然后介绍了对象,对象的创建,内存分配原则,对象回收,访问定位等。通过参数调整JVM到最优状态,

9.1 虚拟机工具 - jps详解

9.2 虚拟机工具 - jstat详解

9.3 虚拟机工具  -  jinfo详解

9.4 虚拟机工具  -  jmap详解 (存堆快照信息)

9.5 虚拟机工具  -  jhat详解 - heap analyse tools

9.6 虚拟机工具  -  jstack详解

9.7 虚拟机工具  -  jConsole详解

9.8 死锁的监控

第十章 visual VM - 监控JVM性能的可视化工具

第十二章、 JVM性能调优案例讲解

案例1 - 一个性能高机器部署一个普通的web应用,因为Full GC的问题,经常有用户反映长时间出现卡顿现象

案例2 - direct memory 内存小溢出问题:不定期的内存溢出,把堆内存加大,也无济于事,导出堆转储快照信息,没有任何信息,内存监控正常,把direct memory改大一些就没有问题了。不要忘记每一块内存区域的存在

案例3 - JVM奔溃,connect reset 问题: 两端消息通讯不对等,中间加消息队列

回顾:原理 + 工具 +  案例

1. JVM运行时区域 = 线程独占区(虚拟机栈 [栈帧,局部变量表],本地方法栈,程序计数器) + 线程共享区(方法区,堆)

2. 内存(对象)的分配回收

2.1 垃圾收集器

serial (单线程,结构简单,效率高)parnew(多线程,和CMS结合) parallel (服务的默认的,针对吞吐量的)cms  (并行的)g1(效率最高的)

2.2 垃圾对象的标记算法

引用计数法(一般不用),可达性分析法

2.3 垃圾收集算法

标记-清除算法(慢)

复制算法(适用于新生代内存)

标记整理算法(适用于老年代内存)

分代收集算法(结合,根据不同代选择不同算法)

3. 对象内存的分配原则

首先在Eden分配; 大对象直接进入老年代;长期存活对象进入老年代;空间分配担保;逃逸分析&栈上分配;

4. 工具

命令行: jps;jstat;jinfo;jmap;jhat;jstack;

图形化: jconsole; visual VM(基于插件)

5. 三个案例


 

第一章 jvm概述

一 知识点体系

二、 jdk, jre, jvm的关系

jdk>jre> jvm   

jre = jvm + java se api

三、内存溢出场景模拟

package heap_error;

import java.util.ArrayList;
import java.util.List;

public class MainTest {
	public static void main(String[] args) {
		List<Demo> demoList = new ArrayList();
		while(true) {
			demoList.add(new Demo());
		}
	}
}

这几个参数分别设置了 导出堆内存镜像,内存大小的设置

-XX:+HeapDumpOnOutOfMemoryError -Xms20m -Xmx20m

 

执行完后在项目下有个这个文件:  java_pid5392.hprof, 是快照,分析内存

分析堆内存镜像,需要用到这个工具定位程序代码出现内存溢出的场景 

64位下载链接: https://www.eclipse.org/downloads/download.php?file=/mat/1.9.0/rcp/MemoryAnalyzer-1.9.0.20190605-win32.win32.x86_64.zip&mirror_id=1142

经过分析,一眼就可以看出demo这个对象内存太大。

四、jvm性能监控场景模拟

package jconsole;

import java.util.ArrayList;
import java.util.List;


public class JconsoleTest {
	// 构造函数
	public JconsoleTest() {
		byte[] b1 = new byte[128 * 1024];
	}
	
	public static void main(String[] args) {
		// 睡眠5s
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		fill(1000);
	}

	private static void fill(int n) {
		List<JconsoleTest> jconsoleList = new ArrayList();
		for(int i = 0; i< n; i++) {
			jconsoleList.add(new JconsoleTest());
		}
	}

}

在jdk bin 目录下有jconsole这个监控工具,直接打开

会是这个东西

了解内存模型,了解垃圾回收机制,了解类分配策略

第二章

一、 Java的历史版本

二、1.8的新特性

    1. lambda函数式编程

package lambda;
import java.awt.Event;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;

public class LambdaTest extends JFrame{
	private JButton jButton;	
	public LambdaTest() {
		this.setBounds(200,200,400,200);
		this.setTitle("点击显示");
		jButton = new JButton("点击");
		this.add(jButton);
		this.setVisible(true);
//		jButton.addActionListener(new ActionListener() {
//			@Override
//			public void actionPerformed(ActionEvent e) {
//				System.out.println("ddddddddddd");
//			}
//		});
		
		jButton.addActionListener(event -> System.out.println("hhhhhh"));
	}
	
	public static void main(String[] args) {
		new LambdaTest();
	}
}

三、Java 虚拟机的发展

第三章 - java 五大 内存区域

一、jvm内存

线程独占区就是每个线程都有这三个区域,自己家的厕所

线程共享区就是每个线程都可以共享, 公共厕所

二、java 内存区域 - 程序计数器 - 行号

三、java 内存区域 - java虚拟机栈 - 存放栈帧

设置栈大小 - 栈内存溢出

不设置栈大小,不断申请内存 - 内存溢出

四、java 内存区域 - - java本地方法栈

五、java 内存区域 - - java堆

六、java 内存区域 - -方法区 

七、java 内存区域 - -方法区 - 常量池

常量池是方法区里面的一块小空间

java 和 c 是由内存动态分配和垃圾回收围起来的高墙

7.1 直接内存 - Nio

 

第四章  对象的创建

Java堆是被所有线程共享的一块内存区域,主要用于存放对象实例,为对象分配内存就是把一块大小确定的内存从堆内存中划分出来,通常有指针碰撞和空闲列表两种实现方式。

1.指针碰撞法
假设Java堆中内存时完整的,已分配的内存和空闲内存分别在不同的一侧,通过一个指针作为分界点,需要分配内存时,仅仅需要把指针往空闲的一端移动与对象大小相等的距离。使用的GC收集器:Serial、ParNew,适用堆内存规整(即没有内存碎片)的情况下。

2.空闲列表法
事实上,Java堆的内存并不是完整的,已分配的内存和空闲内存相互交错,JVM通过维护一个列表,记录可用的内存块信息,当分配操作发生时,从列表中找到一个足够大的内存块分配给对象实例,并更新列表上的记录。使用的GC收集器:CMS,适用堆内存不规整的情况下。

如果想多了,创建一个对象还是挺麻烦的,需要这么多步骤,那么我们在开发过程中尽量非必须的对象创建呢?

创建对象有以下几个要点

  1. 类加载机制检查:JVM首先检查一个new指令的参数是否能在常量池中定位到一个符号引用,并且检查该符号引用代表的类是否已被加载、解析和初始化过
  2. 分配内存:把一块儿确定大小的内存从Java堆中划分出来
  3. 初始化零值:对象的实例字段不需要赋初始值也可以直接使用其默认零值,就是这里起得作用
  4. 设置对象头:存储对象自身的运行时数据,类型指针
  5. 执行<init>:为对象的字段赋值

一、给对象分配内存 - 指针碰撞 & 空闲列表

 

二、线程安全问题 - 线程同步加锁 & 本地线程分配缓冲

内存分配并发问题

在创建对象的时候有一个很重要的问题,就是线程安全,因为在实际开发过程中,创建对象是很频繁的事情,作为虚拟机来说,必须要保证线程是安全的,通常来讲,虚拟机采用两种方式来保证线程安全:

  • CAS: CAS 是乐观锁的一种实现方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。虚拟机采用 CAS 配上失败重试的方式保证更新操作的原子性。
  • TLAB: 为每一个线程预先分配一块内存,JVM在给线程中的对象分配内存时,首先在TLAB分配,当对象大于TLAB中的剩余内存或TLAB的内存已用尽时,再采用上述的CAS进行内存分配。

 

第五章、探究对象的结构

一、垃圾回收几个著名的算法

            复制算法

             标记 - 清除算法

             标记 - 复制算法

             分代收集算法

第六章 对象的访问定位方式- 两种

对象要保存到实例的指针, 也要保存到类的指针

 

第七章 垃圾回收 GC 

7.1 GC概述

垃圾回收算法一般JVM帮我们处理,但是当高并发下,需要提高性能的时候需要更改注意GC,

三个问题:

  • 如何判断一个对象是垃圾对象? - 找到一个可回收的对象

          引用计数法

          可达性分析法

  • 如何回收?

        回收策略算法

             复制算法

             标记 - 清除算法

             标记 - 复制算法

             分代收集算法

        回垃圾回收器

            Serial

            Parnew

            Cms

            G1

  • 何时回收?

7.2 垃圾回收-判断对象是否存活算法-引用计数法详解

本程序系统默认使用  parallel垃圾回收器

1. 定义

2. 存在问题演示

配置run config, 打印jvm信息 :   -verbose:gc -XX:+PrintGCDetails

package gc_引用计数器;

public class Main_index_count {
    private Object instance;
    public static void main(String[] args) {
        // 创建两个对象
        Main_index_count m1 = new Main_index_count();
        Main_index_count m2 = new Main_index_count();
        // 两个对象相互引用
        m1.instance = m2;
        m2.instance = m1;
        // 切断栈变量引用 -   -verbose:gc -XX:+PrintGCDetails
        m1 = null;
        m2 = null;
        System.gc();
    }
}

显示日志

"C:\Program Files\Java\jdk1.8.0_131\bin\java.exe" -verbose:gc -XX:+PrintGCDetails "-javaagent:D:\progmaInstall\IntelliJ IDEA 2018.2.5\lib\idea_rt.jar=10713:D:\progmaInstall\IntelliJ IDEA 2018.2.5\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_131\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar;C:\work\IDEA_WS\JVM\out\production\JVM" gc_引用计数器.Main_index_count -verbose:gc -XX:+PrintGCDetails
[GC (System.gc()) [PSYoungGen: 2987K->808K(28672K)] 2987K->816K(94208K), 0.0028616 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 808K->0K(28672K)] [ParOldGen: 8K->664K(65536K)] 816K->664K(94208K), [Metaspace: 3490K->3490K(1056768K)], 0.0069500 secs] [Times: user=0.03 sys=0.00, real=0.01 secs] 
Heap
 PSYoungGen      total 28672K, used 246K [0x00000000e0980000, 0x00000000e2980000, 0x0000000100000000)
  eden space 24576K, 1% used [0x00000000e0980000,0x00000000e09bd880,0x00000000e2180000)
  from space 4096K, 0% used [0x00000000e2180000,0x00000000e2180000,0x00000000e2580000)
  to   space 4096K, 0% used [0x00000000e2580000,0x00000000e2580000,0x00000000e2980000)
 ParOldGen       total 65536K, used 664K [0x00000000a1c00000, 0x00000000a5c00000, 0x00000000e0980000)
  object space 65536K, 1% used [0x00000000a1c00000,0x00000000a1ca6168,0x00000000a5c00000)
 Metaspace       used 3497K, capacity 4498K, committed 4864K, reserved 1056768K
  class space    used 387K, capacity 390K, committed 512K, reserved 1048576K

Process finished with exit code 0

 引用计数的问题解析

 

7.3 垃圾回收-判断对象是否存活算法-可达性分析法详解

1. 定义

2. 图示解析

 

知道了哪些对象需要回收,接下来就要使用垃圾回收算法了,

7.4 标记-清除算法

两大问题:

1. 空间问题:收集之后空间很零散,重新分配对象时候寻址很难,又的调用一次GC机制,性能降低。

2. 效率问题

 

7.5 复制算法 & 内存浪费的解决方案

核心思想:把活的复制到servior区域,把eden中死的全清除,分配内存在Eden取,完整的片段,不浪费

7.6 标记 - 整理算法

不是直接清除,而是整理再清除,解决了复制算法内存担保的效率慢问题

 

7.7 分代收集算法

内存分新生代和老年代,根据不同的分代选择不同的算法,对新生代内存回收率较高的会选择复制算法,老年代内存回收低,用标记整理算法

 

算法讲完了,接下来讲解关于垃圾回收器相关的问题

7.8 垃圾收集器-serial收集器详解.

整体新能低,对分配内存小,速度快的桌面应用适用

7.9 垃圾收集器-ParNew收集器详解.

7.10 垃圾收集器-parallel scavenge收集器详解.

7.11 垃圾收集器-CMS收集器详解.

7.12 垃圾收集器-最牛的垃圾收集器-g1收集器详解

 

第八章、 内存分配

8.1 内存分配概述

8.2 内存分配-Eden区域 - 对象优先分配到Eden区

public class MainEden {
    public static void main(String[] args) {
        byte[] b1 = new byte[2 * 1024 * 1024]; // 2M
        byte[] b2 = new byte[2 * 1024 * 1024]; // 2M
        byte[] b3 = new byte[2 * 1024 * 1024]; // 2M
        byte[] b4 = new byte[4 * 1024 * 1024]; // 2M

        System.gc();
    }
}

  • -verbose:gc : 打印垃圾收集信息,
  • -XX:+PrintGCDetails :开启垃圾回收打印细节
  •  -XX:+UseSerialGC: 不适用默认的收集器parallel
  •  -Xms20M -Xmx20M : 限制堆内存的大小 在20M
  •  -XX:SurvivorRatio=8:限制EDEN 区域为八

Eden区域是新生代的一块区域

GC 和 full GC的区别,full GC可以system.gc() 或系统调用,频率比GC长的多得多。时间长,

8.1 内存分配-大对象直接进老年代

-XX:PretenureSizehreshold=6M : 自己指定多大的对象才是大对象

大对象 指的是大字符串或者是大数组,Eden区域gc频繁,直接放在老年代GC慢,性能高

8.1 内存分配-长期存活的对象进入老年代

-XX:MaxTenuringThreshold = 15  : 指定相对的存活时间

8.1 内存分配 - 空间分配担保

-XX:+HandlePromotionFailure  :开启空间分配担保, - 代表关闭

1. 先看自己老年代内存够不够新生代的复制

2. 检查内存担保有没有开启

8.1 内存分配-逃逸分析与栈上分配

逃逸分析: 分析对象的作用域,在方法体内,没有逃逸,反之亦然

我们在定义对象的时候,如果定义在方法体中,对象的作用域只在这个方法中,是不发生逃逸的,不逃逸,就把对象的分配直接放到栈内存当中去,执行完出栈,性能高,能创建局部对象就不要放外面引用。

public class StackAllocation {
    public StackAllocation obj;

    /** 方法返回StackAllocation对象, 发生逃逸 */
    public StackAllocation getInstance(){
        return obj == null ? new StackAllocation() : obj;
    }
    /** 为成员属性赋值, 发生逃逸 */
    public void setObj(){
        this.obj = new StackAllocation();
    }
    /** 对象的作用域仅在当前方法有效, 没有发生逃逸 */
    public void useStackAllocation(){
        StackAllocation s = new StackAllocation();
    }
    /** 引用成员变量值, 发生逃逸 */
    public void useStackAllocation2(){
        StackAllocation s = getInstance();
    }
}

第九章、虚拟机工具介绍

我们先介绍了jvm,内存结构,然后介绍了对象,对象的创建,内存分配原则,对象回收,访问定位等。通过参数调整JVM到最优状态,

9.1 虚拟机工具 - jps详解

jps -l   :   显示出来运行主类或者jar文件

jps -m : 显示program运行时接收的参数

jps -v : 显示JVM运行时接收的参数

jps -lmv : 显示JVM运行时接收的参数

9.2 虚拟机工具 - jstat详解

jstat -gcutil 6692

jstat -gcutil 6692 1000 10 每隔1000ms 执行10次

9.3 虚拟机工具  -  jinfo详解

实时查看和调整虚拟机各项参数

C:\Users\Administrator>jinfo
Usage:
    jinfo [option] <pid>
        (to connect to running process)
    jinfo [option] <executable <core>
        (to connect to a core file)
    jinfo [option] [server_id@]<remote server IP or hostname>
        (to connect to remote debug server)

where <option> is one of:
    -flag <name>         to print the value of the named VM flag
    -flag [+|-]<name>    to enable or disable the named VM flag
    -flag <name>=<value> to set the named VM flag to the given value
    -flags               to print VM flags
    -sysprops            to print Java system properties
    <no option>          to print both of the above
    -h | -help           to print this help message

C:\Users\Administrator>jinfo -flag UseSerialGC 4816
-XX:-UseSerialGC         //  表示禁用这个gc收集器

9.4 虚拟机工具  -  jmap详解 (存堆快照信息)

存堆快照信息:

        jmap -dump:format=b, file=d:\a.bin 8264

        -XX:+HeapDumpOnOutOfMemoryError

       jmap -histo 8264 | more        //  more分页查看

9.5 虚拟机工具  -  jhat详解 - heap analyse tools

1. 生成对快照信息

C:\Users\Administrator>jmap -dump:format=b,file=d:\a.bin 6044
Dumping heap to D:\a.bin ...
Heap dump file created

2. jhat进行分析

C:\Users\Administrator>jhat d:\a.bin
Reading from d:\a.bin...
Dump file created Wed Oct 02 22:55:31 CST 2019
Snapshot read, resolving...
Resolving 121119 objects...
Chasing references, expect 24 dots........................
Eliminating duplicate references........................
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.

3. 可视化查看

4. OQL 查询对象

select s from java.lang.String s where s.value.length > 1000

9.6 虚拟机工具  -  jstack详解

用于生成JVM当前时刻线程快照,当前虚拟机内,快照是:每一条线程方法堆栈的集合,目的:定位线程长时间停顿的原因

9.7 虚拟机工具  -  jConsole详解

1.  内存的监控 - 相当于jstat工具

package jconsole;

import java.util.ArrayList;
import java.util.List;


public class JconsoleTest {
	// 构造函数
	public JconsoleTest() {
		byte[] b1 = new byte[128 * 1024];
	}
	
	public static void main(String[] args) {
		// 睡眠5s
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		fill(1000);
	}

	private static void fill(int n) {
		List<JconsoleTest> jconsoleList = new ArrayList();
		for(int i = 0; i< n; i++) {
			jconsoleList.add(new JconsoleTest());
		}
	}

}

2. 线程的监控  -  相当于 jstack那个工具

模拟了三个状态, runable状态,while true 状态, wait状态

package jconsole_thread;

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        /**
         * 名称: main
         * 状态: RUNNABLE
         * 总阻止数: 0, 总等待数: 0
         *
         * 堆栈跟踪:
         * java.io.FileInputStream.readBytes(Native Method)
         * java.io.FileInputStream.read(FileInputStream.java:255)
         * java.io.BufferedInputStream.read1(BufferedInputStream.java:284)
         * java.io.BufferedInputStream.read(BufferedInputStream.java:345)
         */
        //1  输入等待
        Scanner sc = new Scanner(System.in);
        sc.next();

        //2  while true 等待
        /**
         * 名称: while true
         * 状态: RUNNABLE
         * 总阻止数: 0, 总等待数: 0
         *
         * 堆栈跟踪:
         * jconsole_thread.Main$1.run(Main.java:14)
         * java.lang.Thread.run(Thread.java:748)
         */
        new Thread(new Runnable() {
            public void run() {
                while (true){

                }
            }
        }, "while true").start();


        // 3 wait() 等待
        /***
         * 名称: wait
         * 状态: java.lang.Object@26acf562上的WAITING
         * 总阻止数: 0, 总等待数: 1
         *
         * 堆栈跟踪: 
         * java.lang.Object.wait(Native Method)
         * java.lang.Object.wait(Object.java:502)
         * jconsole_thread.Main$2.run(Main.java:53)
         * java.lang.Thread.run(Thread.java:748)
         */
        sc.next();
        waittest(new Object());


    }

    private static void waittest(final Object obj) {
        new Thread(new Runnable() {
            public void run() {
                synchronized (obj){
                    try {
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }, "wait").start();
    }
}

9.8 死锁的监控

1. DeadLock .java

package deadlock;

public class DeadLock implements Runnable {
    // 定义两个资源
    private Object obj1;
    private Object obj2;
    // 构造方法
    public DeadLock(Object obj1, Object obj2) {
        this.obj1 = obj1;
        this.obj2 = obj2;
    }
    // 对两个临界资源进行加锁
    public void run() {
        synchronized (obj1){
            // 死锁只有在非常慢的情况下才会发生,这里睡眠就行了
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            synchronized (obj2){
                System.out.println("死锁演示");
            }
        }
    }
}

2. MainTest .java  测试类 

package deadlock;

public class MainTest {
    public static void main(String[] args) {
        // 定义两个临界资源
        Object obj1 = new Object();
        Object obj2 = new Object();

        // 定义两个线程分别交叉抢占两个临界资源
        new Thread(new DeadLock(obj1, obj2)).start();
        new Thread(new DeadLock(obj2, obj1)).start();

    }
}

第十章 visual VM使用 - 监控JVM性能的可视化工具

下载地址

第十二章、 JVM性能调优案例讲解

案例1一个性能高机器部署一个普通的web应用,因为Full GC的问题,经常有用户反映长时间出现卡顿现象

1 遇到问题

一个性能高机器部署一个普通的web应用,因为Full GC的问题,经常有用户反映长时间出现卡顿现象

2 处理思路

  •  优化SQL - 访问某个功能卡顿时,要这样做,建索引等  (no)
  • JVM监控工具 - 监控CPU(no)
  • JVM监控内存 (yes)

         - 问题是 Full  GC,每次gc时间长,20-30S

三条理论原则: 对象首先在Eden分配, 大对象直接进入老年代,长期存活对象放入老年代。

一年录入的考核数据很大,excel workbook对象大, 大对象直接进入老年代,所以一般的GC是不会回收它的,当老年代的内存不够的时候,平时不看,就某个考核点来用系统,大对象很多,就会发生Full GC,又因为堆内存分配的特别大,导致回收时间很长

3 解决办法 :

把堆内存改小,Full gc回收时间短

部署单机的6个Tomcat的集群,部署多个web服务,每个web容器分配4G的堆内存,

前面加一个Nginx,负载均衡采用IP 哈希的方式,没做session的共享,

4 经验总结

根据不同场景选择不同的GC垃圾回收器,使用监控工具

案例2

数据是类似于彩票,股票这样的数据

1 遇到问题

不定期的内存溢出,把堆内存加大,也无济于事,导出堆转储快照信息,没有任何信息,内存监控正常

把direct memory改大一些就没有问题了。

不要忘记每一块内存区域的存在

2 解决办法

放到一个8G内存,win7系统,问题久没有了,监控发现有个byteBuffer, 它是 NIO里面的,我们知道NIO为了提高性能,它会申请一些堆外内存,因为2G内存小,又把堆内存加大了,于是在进行NIO的时候,我们的直接内存,也就是堆外内存被撑爆了,然后它自己又不能触发垃圾收集,所以导致了内存溢出,不是堆内存溢出,而是操作系统的内存溢出。把direct memory改大一些就没有问题了。

不要忘记每一块内存区域的存在

案例3

积累大量任务,两端信息不对等,JVM奔溃,

中间弄一个消息队列,生产消费者模式

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值