【JavaSE】之JVM入门(下)(一)

简介: 【JavaSE】之JVM入门(下)

前言


本文为JVM入门基础知识,Java全栈学习路线可参考:【Java全栈学习路线】最全的Java学习路线及知识清单,Java自学方向指引,内含最全Java全栈学习技术清单~


本文上接:【JavaSE】之JVM入门(上)


五、堆


1.堆简介


Heap(堆),一个JVM只有一个堆内存,堆内存的大小是可以调节的

类加载器读取了类文件后,一般会把什么东西放到堆中?类、方法、常量、变量、保存我们所有引用类型的真实对象

堆内存中细分为三个区域:新生区(伊甸园区)Young/New;养老区 old;永久区 Perm

869eb5c3a49048f2aafe0fab9cbb15ce.png

2.新生区与养老区


新生区是类诞生,成长,消亡的区域,一个类在这里产生,应用,最后被垃圾回收器收集,结束生命。


新生区又分为两部分:伊甸区(Eden Space)和幸存者区(Survivor Space),所有的类都是在伊甸区被new出来的,幸存区有两个:0区 和 1区,当伊甸园的空间用完时,程序又需要创建对象,JVM的垃圾回收器将对伊甸园区进行垃圾回收(Minor GC)。将伊甸园中的剩余对象移动到幸存0区,若幸存0区也满了,再对该区进行垃圾回收,然后移动到1区,那如果1区也满了呢?(这里幸存0区和1区是一个互相交替的过程)再移动到养老区,若养老区也满了,那么这个时候将产生MajorGC(Full GC),进行养老区的内存清理,若养老区执行了Full GC后发现依然无法进行对象的保存,就会产生OOM异常 “OutOfMemoryError ”。如果出现 java.lang.OutOfMemoryError:java heap space异常,说明Java虚拟机的堆内存不够,原因如下:


1、Java虚拟机的堆内存设置不够,可以通过参数 -Xms(初始值大小),-Xmx(最大大小)来调整。

2、代码中创建了大量大对象,并且长时间不能被垃圾收集器收集(存在被引用)或者死循环。


3.永久区


永久存储区是一个常驻内存区域,用于存放JDK自身所携带的Class,Interface的元数据,也就是说它存储的是运行环境必须的类信息,被装载进此区域的数据是不会被垃圾回收器回收掉的,关闭JVM才会释放此区域所占用的内存。

如果出现 java.lang.OutOfMemoryError:PermGen space,说明是Java虚拟机对永久代Perm内存设置不够。一般出现这种情况,都是程序启动需要加载大量的第三方jar包,例如:在一个Tomcat下部署了太多的应用。或者大量动态反射生成的类不断被加载,最终导致Perm区被占满。


4.永久代与元空间


什么是永久代和元空间?


方法区是一种规范,不同的虚拟机厂商可以基于规范做出不同的实现,永久代和元空间就是出于不同jdk版本的实现。


方法区就像是一个接口,永久代与元空间分别是两个不同的实现类。

只不过永久代是这个接口最初的实现类,后来这个接口一直进行变更,直到最后彻底废弃这个实现类,由新实现类—元空间进行替代。


jdk1.8之前:


84c398de813f4a58a7bd185ed1e86177.png

jdk1.8以及之后:在堆内存中,逻辑上存在,物理上不存在(元空间使用的是本地内存)

039778bf76cc42fe86be195234aee380.png


5.常量池


  • 在jdk1.7之前,运行时常量池+字符串常量池是存放在方法区中,HotSpot VM对方法区的实现称为永久代


f16b5d7a55624527895c23ebb1742a0a.png

在jdk1.7中,字符串常量池从方法区移到堆中,运行时常量池保留在方法区中


c7b55ccbe97f46fda61574b08e01015c.png

jdk1.8之后,HotSpot移除永久代,使用元空间代替;此时字符串常量池保留在堆中,运行时常量池保留在方法区中,只是实现不一样了,JVM内存变成了直接内存

12f39f1fb42c41c1997b0ab24a3178f2.png


6.堆内存调优


-Xms:设置初始分配大小,默认为物理内存的 “1/64”。

-Xmx:最大分配内存,默认为物理内存的 “1/4”。

-XX:+PrintGCDetails:输出详细的GC处理日志。


代码查看内存使用情况:


默认的情况下分配的内存是总内存的 1/4,而初始化的内存为 1/64

public class Demo01 {
    public static void main(String[] args) {
        // 返回虚拟机试图使用的最大内存
        long max = Runtime.getRuntime().maxMemory();    // 字节:1024*1024
        // 返回jvm的总内存
        long total = Runtime.getRuntime().totalMemory();
        System.out.println("max=" + max + "字节\t" + (max/(double)1024/1024) + "MB");
        System.out.println("total=" + total + "字节\t" + (total/(double)1024/1024) + "MB");
        // 默认情况下:分配的总内存是电脑内存的1/4,初始化的内存是电脑的1/64
    }
}

IDEA中进行VM调优参数设置(设置完成后需要重启IDEA):

ac8413910ed04845855e6448d9e98978.png

26a1c381fa72486ab30b5433599f8f52.png


VM参数调优:把初始内存,和总内存都调为 1024M,运行,查看结果!


-Xms1024m -Xmx1024m -XX:+PrintGCDetails


928ec9955c2a4f38b37d01ef4d8e6257.png


经计算得到:新生区加老年区内存总量等于jvm的总内存,可以证明:元空间并不在虚拟机中,而是使用本地内存。


44760ca529064621b66cd25b5982ce7f.png


六、使用JProfiler工具分析OOM原因

1.Dump内存快照


在运行java程序的时候,有时候想测试运行时占用内存情况,这时候就需要使用测试工具查看了。在eclipse里面有 Eclipse Memory Analyzer tool(MAT)插件可以测试,而在idea中也有这么一个插件,就是JProfiler,一款性能瓶颈分析工具!


作用:


分析Dump文件,快速定位内存泄漏

获得堆中对象的统计数据

获得对象相互引用的关系

采用树形展现对象间相互引用的情况


2.安装JProfiler


IDEA插件安装


eff5675da5034ee595844e2a5c4a9171.png


安装JProfiler监控软件

下载地址:https://www.ej-technologies.com/download/jprofiler/version_92


b17a4c092e434e37abb284cfcc6cb46f.png


可以使用以下注册码:

// 注册码仅供参考
L-Larry_Lau@163.com#23874-hrwpdp1sh1wrn#0620
L-Larry_Lau@163.com#36573-fdkscp15axjj6#25257
L-Larry_Lau@163.com#5481-ucjn4a16rvd98#6038
L-Larry_Lau@163.com#99016-hli5ay1ylizjj#27215
L-Larry_Lau@163.com#40775-3wle0g1uin5c1#0674

配置IDEA运行环境

Settings–Tools–JProflier–JProflier executable选择JProfile安装可执行文件。(如果系统只装了一个版本, 启动IDEA时会默认选择)保存。

9b0c89d003204dde9278ec72f671e181.png

3.JProfiler使用测试


  • 编写问题代码进行测试
package com.wang.JVM.Demo02;
import java.util.ArrayList;
public class Demo03 {
    byte[] byteArray = new byte[1*1024*1024]; // 1M = 1024K
    public static void main(String[] args) {
        ArrayList<Demo03> list = new ArrayList<>();
        int count = 0;
        try {
            while (true) {
                list.add(new Demo03());  // 问题所在
                count = count + 1;
            }
        } catch (Error e) {
            System.out.println("count:" + count);
            e.printStackTrace();
        }
    }
}


设置vm参数

-Xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError

5e39351d5baa4cc1a5966a5dbdc3faee.png

设置完后再运行问题代码发生内存溢出时会自动生成dump文件


4ae6a9ee6cbe47aa81b37a3944f6f3fa.png


使用 Jprofiler 工具分析查看

双击.hprof文件默认使用 Jprofiler 进行 Open大的对象!


7a12223830f34f25a135bb8308e738ce.png

d17a74dfb3b946b0b54fcff56dc2d088.png


cc41b75e1ac64bd2b3db8a46f3b23047.png


从软件开发的角度上,dump文件就是当程序产生异常时,用来记录当时的程序状态信息(例如堆栈的状态),用于程序开发定位问题

相关文章
|
7月前
|
Oracle Java 编译器
基本概念【入门、 发展简史、核心优势、各版本的含义、特性和优势、JVM、JRE 和 JDK 】(二)-全面详解(学习总结---从入门到深化)
基本概念【入门、 发展简史、核心优势、各版本的含义、特性和优势、JVM、JRE 和 JDK 】(二)-全面详解(学习总结---从入门到深化)
94 1
|
7天前
|
监控 架构师 Java
Java虚拟机调优的艺术:从入门到精通####
本文作为一篇深入浅出的技术指南,旨在为Java开发者揭示JVM调优的神秘面纱,通过剖析其背后的原理、分享实战经验与最佳实践,引领读者踏上从调优新手到高手的进阶之路。不同于传统的摘要概述,本文将以一场虚拟的对话形式,模拟一位经验丰富的架构师向初学者传授JVM调优的心法,激发学习兴趣,同时概括性地介绍文章将探讨的核心议题——性能监控、垃圾回收优化、内存管理及常见问题解决策略。 ####
|
6月前
|
存储 监控 算法
|
7月前
|
缓存 运维 监控
JVM的基础入门(下)
JVM的基础入门(下)
99 0
|
7月前
|
Java 编译器 对象存储
java一分钟之Java入门:认识JDK与JVM
【5月更文挑战第7天】本文介绍了Java编程的基础——JDK和JVM。JDK是包含编译器、运行时环境、类库等的开发工具包,而JVM是Java平台的核心,负责执行字节码并实现跨平台运行。常见问题包括版本不匹配、环境变量配置错误、内存溢出和线程死锁。解决办法包括选择合适JDK版本、正确配置环境变量、调整JVM内存参数和避免线程死锁。通过代码示例展示了JVM内存管理和基本Java程序结构,帮助初学者更好地理解JDK和JVM在Java编程中的作用。
79 0
|
7月前
|
缓存 监控 Java
Java从入门到精通:3.3.1性能优化与调优——学习Java的性能优化技巧,如JVM调优
Java从入门到精通:3.3.1性能优化与调优——学习Java的性能优化技巧,如JVM调优
|
7月前
|
算法 安全 前端开发
JVM的基础入门(上)
JVM的基础入门(上)
117 0
|
7月前
|
Oracle IDE Java
基本概念【入门、 发展简史、核心优势、各版本的含义、特性和优势、JVM、JRE 和 JDK 】(二)-全面详解(学习总结---从入门到深化)(下)
基本概念【入门、 发展简史、核心优势、各版本的含义、特性和优势、JVM、JRE 和 JDK 】(二)-全面详解(学习总结---从入门到深化)
68 1
|
7月前
|
存储 监控 算法
JVM入门手册(通俗版)
JVM入门手册(通俗版)
60 0
|
7月前
|
Java 程序员 PHP
基本概念【入门、 发展简史、核心优势、各版本的含义、特性和优势、JVM、JRE 和 JDK 】(二)-全面详解(学习总结---从入门到深化)(上)
基本概念【入门、 发展简史、核心优势、各版本的含义、特性和优势、JVM、JRE 和 JDK 】(二)-全面详解(学习总结---从入门到深化)
63 0