热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JVM学习——内存空间(学习过程)

JVM——内存空间关于内存的内容,内存的划分。JVM1.71.8的变化比较大JVM指令执行的时候,是基于栈的操作。每一个方法执行的时候,都会有一个属于自己的栈帧的数据结构
本文详细介绍了JVM-内存空间涉及的知识点,如堆,栈,方法区等知识点和相关JVM工具的简单介绍

JVM——内存空间

关于内存的内容,内存的划分。JVM1.7 -> 1.8的变化比较大

JVM指令执行的时候,是基于栈的操作。每一个方法执行的时候,都会有一个属于自己的栈帧的数据结构。栈的深度,在编译为Class文件的时候,就已经能够确定好了。还能够看到栈的最大深度。

要涉及的知识点(学习路线):

JVM包含的内存空间布局

  1. 虚拟机栈:Stack Frame 栈帧。引用本身是一个变量,在虚拟机栈中存储。
  2. 程式计数器(Program Counter)。
  3. 本地方法栈:native,主要用于执行本地方法。
  4. 堆(heap):最大的空间-共享区域。存放一个个的对象实例。与堆相关的一个重要概念是垃圾收集器。现在几乎所有的垃圾收集器都是采用分代收集算法。所以堆空间也基于这一点进行了相应的划分:新生代与老年代。Eden空间,From Survivor空间与To Survivor空间。
  5. 方法区(method Area):存储元信息。永久代(Permanent Generation)从JDK1.8开始,已经彻底废弃了永久代。使用元空间(meta space)来替代。
  6. 运行时的常量池:方法区的一部分。
  7. 直接内存:Direct Memory。不是JVM管理的,是由计算机或者硬件来操作的。JVM只是能够申请一片内存区使用。与Java NIO密切相关。JVM通过堆上的DirectByteBuffer来操作直接内存。

Java对象内存分配原理与布局

一个对象由两部分组成:数据,元数据。(不在同一个地方)。元数据是存在堆的方法区的。

对象的两种实现方式

image-20200217210728517

  1. 指针指向 两个都是指针的
  2. 指针指向 一个指针的

堆的压缩:涉及到对象的移动。如果对象发生移动,地址就会变了。如果采用上面第一次,就会经常发生变化。如果用第二种,第二种就算位置变,也只是引用的位置变。所以Oracle的HotSpot采用的是第二种实现方式。

image-20200217210952728

  • 程序计数器和栈 都是线程私有的。
  • 如果是8个基本类型的值,是直接放在栈里面的。
  • 引用是放在栈里面的。生成的对象是方法
  • 本地方法栈:主要是跟native方法有关的。 和虚拟机栈没什么本质区别。
  • 堆是主要的内存空间。堆内存的空间是共享的。
  • 老年代和新年待。老年代的回收频率比新生代频率低的很多。
  • java的堆空间,在物理空间上,既可以是连续的。也可以是不连续的
  • 方法区:不是存放方法的区域。主要存放的是元信息。元信息和永久代不是一个概念。永久代永远不会被回收掉。元信息也是一般情况下,不会被收集的。这也是容易混淆的原因。但是比如Class的元信息。在Class被卸载的时候,元信息也是会被回收的。

关于Java对象创建的过程:

new 关键字创建对象的三个步骤:

  1. 在堆内存中创建出对象实例
  2. 为对象的成员变量赋初始值
  3. 将对象的引用返回
  • 指针碰撞(前提是堆中的空间通过一个指针进行分割,一侧是已经被占用的空间,另一侧是未被占用的空间)
  • 空闲列表(前提是堆内存空间中已被使用与未被使用的空间是交织在一起的,这时,虚拟机就需要通过一个列表来记录哪些空间是可使用的,哪些空间是已经被使用的,接下来找出可以容纳下新创建对象的且未被使用的空间,在此空间存放该对象。同时还要修改列表上的记录)
  • 对象在内存中的布局
    • 对象头
    • 实例数据(即我们在一个类中所声明的各项信息)
    • 对其填充(可选)
  • 引用访问对象的方式:
    1. 使用句柄的方式。
    2. 使用直接指针的方式。

堆内存溢出异常

运行此程序,内存溢出:(此时电脑会起飞)

package com.dawa.jvm.memory;

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

public class MyTest {
    public static void main(String[] args) {
        List list = new ArrayList<>();

        for (; ; ) {
            list.add(new MyTest());
        }
    }
}

使用虚拟机参数设定整个JVM对空间的大小,并且溢出的时候,打印到硬盘上(转储)。

参数:-Xms5m -Xmx5m -XX:+HeapDumpOnOutOfMemoryError

image-20200218165638081

溢出异常

image-20200218171015703

使用jvisualVM工具:(我本地Mac打不开,可能跟安装的版本有关) 还有个 Jconsole

Last login: Tue Feb 18 17:25:06 on ttys000
➜  ~ jvisualvm 
Unable to locate an executable at "/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/bin/jvisualvm" (-1)
➜  ~ 

image-20200218172712586

image-20200218172756599

image-20200218172830687

当我们内存溢出的时候,我们可以很好的借助这种工具来判断是哪个类运行的多,哪个类浪费了空间。

接下来,我们加一行 System.gc()

image-20200218173212181

再次运行的时候:内存不会溢出

image-20200218173808779

内存没满

并且垃圾回收活动很频繁

堆空间大小:一直保持在2m左右

还有一个工具:Jconsole(如下图)

image-20200218175118382

虚拟机栈溢出异常

虚拟机栈溢出案例

使用递归,并且设置虚拟机栈的大小:

image-20200218175756654

设置虚拟机栈大小的虚拟机参数:-Xss100k

栈大小最少需要160K。

image-20200218180031154

设置为160k

image-20200218180111324

所以:运行之后就直接栈溢出。Stack Overflow

image-20200218180139878

image-20200218181543093

点击右上角的Dump。抓取一些线程的信息

image-20200218181607054

看看就行了。

这些工具就在这里,一些人不知道,所以没用过。如果有需要,就拿去用。

JConsole 工具的使用

image-20200218181948462

还提供了 检测死锁的功能。

image-20200218182203403

如何用代码写出死锁,检测死锁和分析工具的深度解析

死锁:产生的条件:两个线程,互相持有相应的锁,对象去访问

package com.dawa.jvm.memory;

public class MyTest3 {
    public static void main(String[] args) {
        new Thread(A::method,"Thread-A").start();
        new Thread(B::method,"Thread-A").start();
    }
}
//方法的锁,是所在类的锁.
class A{
    public static synchronized void method(){
        System.out.println("method from A");
        try {
            Thread.sleep(5000);
            B.method();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}
class B{
    public static synchronized void method(){
        System.out.println("method from B");
        A.method();
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

image-20200218194620929

image-20200218194451792

JVisualVM软件更好使,进来之后,直接提示线程死锁。(Dump一下)

image-20200218194911047

image-20200218194940860

准备锁定的和已经锁定的。AB线程相互相反

image-20200218195113617

over,合理利用工具。

方法区产生内存溢出异常

采用手段

  1. 显示设定元空间的大小。让它不会自动扩展

  2. 因为存放的都是数据的元信息。采取特殊手段进行(运行期动态生成类 如CGlib)

  3. 方法区产生内存溢出异常.
    在运行期间不断的生成新的对象,元数据会不断的放入元空间.
    元空间默认占用内存21兆.如过不修改配置,会自动扩容(扩容到物理内存大小)
    
  4. 创建一个程序,不断的创建子类。元信息会不断的放入元空间当中。

  5. 虚拟机参数设置:

    ![image-20200218200626828](/Users/shangyifeng/Library/Application Support/typora-user-images/image-20200218200626828.png)

  6. 编写程序:

    image-20200218200508824

查看结果: Metaspace(1.7版本之前,是老年代。所以看网上文字的时候,注意时效性和带着批判的眼光去学习)

image-20200218200719744

使用Jconsole 连接此线程

image-20200218200934605

如果勾选,详细输出,那么控制台也会打印很多东西:

image-20200218201126530

通过CGlib 在运行过程中生成的类:

image-20200218201216049

使用JVisualVM来查看 元空间的变化呈现上升趋势

image-20200218201348523

当到达200M的上线的时候,程序退出

image-20200218201451402

那么,元空间到底是什么?

https://www.infoq.cn/article/Java-PERMGEN-Removed/

通过这篇文章,对其理解。可以理清JDK8中去除永久代的重要事实。

JMap 和Jstat和JCMD的使用

JDK提供的一些命令行工具。

jmap

➜  ~ jmap
Usage:
    jmap [option] 
        (to connect to running process)
    jmap [option] 
        (to connect to a core file)
    jmap [option] [server_id@]
        (to connect to remote debug server)

where 

列出所有的进程,找到端口号

ps -ef | grep java

➜  ~ ps -ef | grep java
  501  4619  1183   0  7:44下午 ??         0:36.29 /Library/Java/JavaVirtualMachines/jdk1.8.0_231.jdk/Contents/Home/bin/java -XX:MaxMetaspaceSize=256m -XX:+HeapDumpOnOutOfMemoryError -Xmx512m -Dfile.encoding=UTF-8 -Duser.country=CN -Duser.language=zh -Duser.variant -cp /Users/shangyifeng/.gradle/wrapper/dists/gradle-5.2.1-all/bviwmvmbexq6idcscbicws5me/gradle-5.2.1/lib/gradle-launcher-5.2.1.jar org.gradle.launcher.daemon.bootstrap.GradleDaemon 5.2.1
  501  5368  4053   0  8:41下午 ttys000    0:00.00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn java
➜  ~

image-20200218204305829

直接通过Jmap获取当前线程的类加载器

jmap -heap 端口号:4619

image-20200218204757683

下一个工具:

jstat

➜  ~ jstat
invalid argument count
Usage: jstat -help|-options
       jstat -

image-20200218205120577

MC:Current Metaspace:当前元空间的大小

MU:Metaspace Utillization :已经被使用的元空间大小

查看当前系统的进程(通用的操作系统的命令)

ps -ef | grep java

JSP命令:查看当前操作系统中的所有进程

➜  ~ jps
5584 Jps
4619 GradleDaemon
1183

JCMD指令

➜  ~ jcmd
5602 sun.tools.jcmd.JCmd
4619 org.gradle.launcher.daemon.bootstrap.GradleDaemon 5.2.1
1183
➜  ~

从JDK1.7开始引入。

➜  ~ jcmd -help
Usage: jcmd  
   or: jcmd -l
   or: jcmd -h

  command must be a valid jcmd command for the selected jvm.
  Use the command "help" to see which commands are available.
  If the pid is 0, commands will be sent to all Java processes.
  The main class argument will be used to match (either partially
  or fully) the class used to start Java.
  If no options are given, lists Java processes (same as -p).

  PerfCounter.print display the counters exposed by this process
  -f  read and execute commands from the file
  -l  list JVM processes on the local machine
  -h  this help
➜  ~

JCMD(常用命令)详解

  1. 查看JVM运行参数 jcmd pid VM.flags

    image-20200218210213471

  2. 列出当前进程,我们能对此进程的操作 jcmd pid help

    image-20200218210628687

  3. 查看当前某个进程的具体命令的具体参数。 jcmd pid JFR.dump

    image-20200218211022283

  4. 列出JVM xing能相关的一些参数jcmd pid PerfCounter.print

    image-20200218211147881

  5. 查看JVM的启动时长jcmd pid VM.uptime

    image-20200218211431934

  6. 查看系统中类的统计信息 jcmd pid GC.class_historgram

    image-20200218211452501

  7. 查看当前线程的堆栈信息 Thred.print

  8. 导出hprof文件,导出的文件可以通过JvisualVM查看。dump jcmd pid GC.heap_dump [filepath]

    image-20200218212008935

  9. 查看VM的信息。 jcmd pid VM.system_properties

    image-20200218212413207

  10. 查看目标JVM进程的版本信息。jcmd pid VM.version

    image-20200218212617064

  11. 查看JVM启动的时候携带的参数。jcm pid VM.command_line

    image-20200218212657397

综上,通过JUI能看到的东西,在命令行都是能够出来的。但是有些时候是不能用UI工具的。JDK在官方发布的时候,不只是源码,还携带了很多便携性的工具。

jstack 专门用来查看或者导出Java应用中线程的堆栈信息。

jstack pid

➜  ~  jstack
Usage:
    jstack [-l] 
        (to connect to running process)
    jstack -F [-m] [-l] 
        (to connect to a hung process)
    jstack [-m] [-l]  
        (to connect to a core file)
    jstack [-m] [-l] [server_id@]
        (to connect to a remote debug server)

Options:
    -F  to force a thread dump. Use when jstack  does not respond (process is hung)
    -m  to print both java and native frames (mixed mode)
    -l  long listing. Prints additional information about locks
    -h or -help to print this help message
➜  ~

image-20200218212907517

Jmc 和 Jhat 工具的使用

另外两个可视化工具,比JvisualVM显示的内容更多,并且在运行的时候也能够使用。

jmc : Java mission Contrl (我的OpenJDK 打不开)

image-20200218213052467

image-20200218213232472

![image-20200218213323367](/Users/shangyifeng/Library/Application Support/typora-user-images/image-20200218213323367.png)image-20200218213323548

image-20200218213353707

相当于把所有的jcmd继承在这个 诊断命令这里了

飞行记录器: jfr : java fly recoard. (下图是截取的一段飞行记录器)

image-20200218213611470

image-20200218213643149

image-20200218213702635

热点方法,加载,异常。编译,调用树。等都能查看的非常非常详细。

不管一个人对JVM多了解,一般都是借助UI工具来进行调优的。

借助 此工具,再看一下源空间的数据

image-20200218213927161

Jhat 工具 对堆存储文件的分析工具

jhat filepath

➜  ~ jhat -help
Usage:  jhat [-stack ] [-refs ] [-port ] [-baseline ] [-debug ] [-version] [-h|-help] 

	-J          Pass  directly to the runtime system. For
			  example, -J-mx512m to use a maximum heap size of 512MB
	-stack false:     Turn off tracking object allocation call stack.
	-refs false:      Turn off tracking of references to objects
	-port :     Set the port for the HTTP server.  Defaults to 7000
	-exclude :  Specify a file that lists data members that should
			  be excluded from the reachableFrom query.
	-baseline : Specify a baseline object dump.  Objects in
			  both heap dumps with the same ID and same class will
			  be marked as not being "new".
	-debug :     Set debug level.
			    0:  No debug output
			    1:  Debug hprof file parsing
			    2:  Debug hprof file parsing, no server
	-version          Report version number
	-h|-help          Print this help and exit
	            The file to read

For a dump file that contains multiple heap dumps,
you may specify which dump in the file
by appending "#" to the file name, i.e. "foo.hprof#3".

All boolean options default to "true"
➜  ~

执行命令,启动一个服务器能够浏览器直接访问。

image-20200218214516979

提供了OQL查询,以一种SQL查询的方式,查询相关的数据。相关语法结构等到用的时候现用现学就行了。

![image-20200218214743118](/Users/shangyifeng/Library/Application Support/typora-user-images/image-20200218214743118.png)

这个功能在JvisualVM里面也有所体现。

image-20200218214828924


推荐阅读
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • JVM 学习总结(三)——对象存活判定算法的两种实现
    本文介绍了垃圾收集器在回收堆内存前确定对象存活的两种算法:引用计数算法和可达性分析算法。引用计数算法通过计数器判定对象是否存活,虽然简单高效,但无法解决循环引用的问题;可达性分析算法通过判断对象是否可达来确定存活对象,是主流的Java虚拟机内存管理算法。 ... [详细]
  • 浏览器中的异常检测算法及其在深度学习中的应用
    本文介绍了在浏览器中进行异常检测的算法,包括统计学方法和机器学习方法,并探讨了异常检测在深度学习中的应用。异常检测在金融领域的信用卡欺诈、企业安全领域的非法入侵、IT运维中的设备维护时间点预测等方面具有广泛的应用。通过使用TensorFlow.js进行异常检测,可以实现对单变量和多变量异常的检测。统计学方法通过估计数据的分布概率来计算数据点的异常概率,而机器学习方法则通过训练数据来建立异常检测模型。 ... [详细]
  • 如何用JNI技术调用Java接口以及提高Java性能的详解
    本文介绍了如何使用JNI技术调用Java接口,并详细解析了如何通过JNI技术提高Java的性能。同时还讨论了JNI调用Java的private方法、Java开发中使用JNI技术的情况以及使用Java的JNI技术调用C++时的运行效率问题。文章还介绍了JNIEnv类型的使用方法,包括创建Java对象、调用Java对象的方法、获取Java对象的属性等操作。 ... [详细]
  • 本文讨论了在VMWARE5.1的虚拟服务器Windows Server 2008R2上安装oracle 10g客户端时出现的问题,并提供了解决方法。错误日志显示了异常访问违例,通过分析日志中的问题帧,找到了解决问题的线索。文章详细介绍了解决方法,帮助读者顺利安装oracle 10g客户端。 ... [详细]
  • Java 11相对于Java 8,OptaPlanner性能提升有多大?
    本文通过基准测试比较了Java 11和Java 8对OptaPlanner的性能提升。测试结果表明,在相同的硬件环境下,Java 11相对于Java 8在垃圾回收方面表现更好,从而提升了OptaPlanner的性能。 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • Java String与StringBuffer的区别及其应用场景
    本文主要介绍了Java中String和StringBuffer的区别,String是不可变的,而StringBuffer是可变的。StringBuffer在进行字符串处理时不生成新的对象,内存使用上要优于String类。因此,在需要频繁对字符串进行修改的情况下,使用StringBuffer更加适合。同时,文章还介绍了String和StringBuffer的应用场景。 ... [详细]
  • 如何搭建Java开发环境并开发WinCE项目
    本文介绍了如何搭建Java开发环境并开发WinCE项目,包括搭建开发环境的步骤和获取SDK的几种方式。同时还解答了一些关于WinCE开发的常见问题。通过阅读本文,您将了解如何使用Java进行嵌入式开发,并能够顺利开发WinCE应用程序。 ... [详细]
  • 本文介绍了H5游戏性能优化和调试技巧,包括从问题表象出发进行优化、排除外部问题导致的卡顿、帧率设定、减少drawcall的方法、UI优化和图集渲染等八个理念。对于游戏程序员来说,解决游戏性能问题是一个关键的任务,本文提供了一些有用的参考价值。摘要长度为183字。 ... [详细]
  • 本文整理了315道Python基础题目及答案,帮助读者检验学习成果。文章介绍了学习Python的途径、Python与其他编程语言的对比、解释型和编译型编程语言的简述、Python解释器的种类和特点、位和字节的关系、以及至少5个PEP8规范。对于想要检验自己学习成果的读者,这些题目将是一个不错的选择。请注意,答案在视频中,本文不提供答案。 ... [详细]
  • 本文介绍了一道经典的状态压缩题目——关灯问题2,并提供了解决该问题的算法思路。通过使用二进制表示灯的状态,并枚举所有可能的状态,可以求解出最少按按钮的次数,从而将所有灯关掉。本文还对状压和位运算进行了解释,并指出了该方法的适用性和局限性。 ... [详细]
  • 面试经验分享:华为面试四轮电话面试、一轮笔试、一轮主管视频面试、一轮hr视频面试
    最近有朋友去华为面试,面试经历包括四轮电话面试、一轮笔试、一轮主管视频面试、一轮hr视频面试。80%的人都在第一轮电话面试中失败,因为缺乏基础知识。面试问题涉及 ... [详细]
  • 数据结构与算法的重要性及基本概念、存储结构和算法分析
    数据结构与算法在编程领域中的重要性不可忽视,无论从事何种岗位,都需要掌握数据结构和算法。本文介绍了数据结构与算法的基本概念、存储结构和算法分析。其中包括线性结构、树结构、图结构、栈、队列、串、查找、排序等内容。此外,还介绍了图论算法、贪婪算法、分治算法、动态规划、随机化算法和回溯算法等高级数据结构和算法。掌握这些知识对于提高编程能力、解决问题具有重要意义。 ... [详细]
author-avatar
轩风羽_609
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有