深入理解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 问题: 两端消息通讯不对等,中间加消息队列

深入理解JVM(一)_第1张图片

回顾:原理 + 工具 +  案例

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(一)_第2张图片


 

第一章 jvm概述

一 知识点体系

深入理解JVM(一)_第3张图片

二、 jdk, jre, jvm的关系

jdk>jre> jvm   

jre = jvm + java se api

深入理解JVM(一)_第4张图片

三、内存溢出场景模拟

package heap_error;

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

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

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

-XX:+HeapDumpOnOutOfMemoryError -Xms20m -Xmx20m

 深入理解JVM(一)_第5张图片

深入理解JVM(一)_第6张图片

执行完后在项目下有个这个文件:  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

深入理解JVM(一)_第7张图片

经过分析,一眼就可以看出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 jconsoleList = new ArrayList();
		for(int i = 0; i< n; i++) {
			jconsoleList.add(new JconsoleTest());
		}
	}

}

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

深入理解JVM(一)_第8张图片

会是这个东西

深入理解JVM(一)_第9张图片

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

第二章

一、 Java的历史版本

深入理解JVM(一)_第10张图片

深入理解JVM(一)_第11张图片

二、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内存

深入理解JVM(一)_第12张图片

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

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

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

深入理解JVM(一)_第13张图片

深入理解JVM(一)_第14张图片

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

深入理解JVM(一)_第15张图片

深入理解JVM(一)_第16张图片

深入理解JVM(一)_第17张图片

设置栈大小 - 栈内存溢出

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

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

五、java 内存区域 - - java堆

深入理解JVM(一)_第18张图片

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

深入理解JVM(一)_第19张图片

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

深入理解JVM(一)_第20张图片

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

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

深入理解JVM(一)_第21张图片

深入理解JVM(一)_第22张图片

7.1 直接内存 - Nio

 

第四章  对象的创建

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

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

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

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

创建对象有以下几个要点

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

深入理解JVM(一)_第23张图片

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

深入理解JVM(一)_第24张图片

深入理解JVM(一)_第25张图片

 

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

内存分配并发问题

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

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

深入理解JVM(一)_第26张图片

 

第五章、探究对象的结构

深入理解JVM(一)_第27张图片

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

            复制算法

             标记 - 清除算法

             标记 - 复制算法

             分代收集算法

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

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

深入理解JVM(一)_第28张图片

 

第七章 垃圾回收 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

 引用计数的问题解析

深入理解JVM(一)_第29张图片

 

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

1. 定义

深入理解JVM(一)_第30张图片

2. 图示解析

深入理解JVM(一)_第31张图片

 

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

7.4 标记-清除算法

两大问题:

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

2. 效率问题

深入理解JVM(一)_第32张图片

 

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

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

深入理解JVM(一)_第33张图片

深入理解JVM(一)_第34张图片

深入理解JVM(一)_第35张图片

深入理解JVM(一)_第36张图片

深入理解JVM(一)_第37张图片

7.6 标记 - 整理算法

深入理解JVM(一)_第38张图片

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

 

7.7 分代收集算法

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

 

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

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

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

深入理解JVM(一)_第39张图片

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

深入理解JVM(一)_第40张图片

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

深入理解JVM(一)_第41张图片

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

深入理解JVM(一)_第42张图片

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

深入理解JVM(一)_第43张图片

 

第八章、 内存分配

8.1 内存分配概述

深入理解JVM(一)_第44张图片

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慢,性能高

深入理解JVM(一)_第45张图片

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到最优状态,

深入理解JVM(一)_第46张图片

9.1 虚拟机工具 - jps详解

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

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

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

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

深入理解JVM(一)_第47张图片

9.2 虚拟机工具 - jstat详解

jstat -gcutil 6692

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

深入理解JVM(一)_第48张图片

9.3 虚拟机工具  -  jinfo详解

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

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

where 

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. 可视化查看

深入理解JVM(一)_第49张图片

4. OQL 查询对象

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

深入理解JVM(一)_第50张图片

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 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(一)_第51张图片

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

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

深入理解JVM(一)_第52张图片

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

深入理解JVM(一)_第53张图片

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

1 遇到问题

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

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

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

2 解决办法

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

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

案例3

深入理解JVM(一)_第54张图片

深入理解JVM(一)_第55张图片

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

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

你可能感兴趣的:(JVM)