java内存分析
对于LINUX类主机JAVA应用程序占用CPU内存过高的分析方法介绍

对于LINUX类主机JAVA应用程序占用CPU内存过高的分析方法介绍当一个Java应用程序在LINUX类主机上占用过高的CPU和内存时,需要进行分析以找出导致问题的根本原因。
下面我将介绍一些分析方法,来帮助您定位问题并进行相应的优化。
1.CPU使用率分析a. 使用top命令来监视进程的CPU使用率。
对于Java应用程序,通常可以找到以java进程的形式运行的应用程序。
通过top命令,您可以查看每个进程的CPU使用率,并根据使用率高低来确定具体的问题进程。
b. 使用htop命令来进行更详细的分析。
htop可以显示进程的层级结构,以及进程之间的关系。
这样可以更容易地找到可能引起问题的进程,例如可能存在的线程死锁或者系统负荷过高的情况。
c. 使用perf工具来进行进一步的性能分析。
perf是一个强大的性能分析工具,可以在Linux系统中进行各种性能监测、性能追踪和调优等工作。
它可以帮助您更好地了解Java应用程序的运行情况,包括函数调用关系、热点代码和性能瓶颈等信息。
2.内存使用分析a. 使用top命令来监测进程的内存使用情况。
通过top命令,您可以查看每个进程的内存使用情况,并根据RES或VIRT列来确定使用内存较多的进程。
b. 使用pmap命令来查看进程的内存映射。
pmap命令可以列出每个进程的内存映射情况,包括栈、堆、共享库等信息。
通过比较不同进程的内存映射情况,可以进一步判断可能存在的内存泄漏或者内存碎片化问题。
c. 使用jstat命令来监视Java应用程序的内存使用情况。
jstat是JDK中的一个命令行工具,可以显示与Java虚拟机(JVM)相关的各种统计信息,包括堆内存、非堆内存、GC情况等。
通过分析jstat命令输出的信息,可以更好地了解Java应用程序的内存使用情况。
3.GC日志分析a. 启用GC日志并分析。
通过在Java应用程序的启动参数中加入-XX:+PrintGCDetails和-XX:+PrintGCDateStamps等参数,可以启用GC日志输出。
Java虚拟机内存分配探析

量分 配 内存空 间 ,当超 过变 量 的作用 域后 , v 会 自动 释放 掉 J a a
为 该 变 量 所 分 配 的 内 存 空 间 ,该 内 存 空 间 可 以 立 即被 另 作 他
用。
享 与 两个 对象 的 引用 同时指 向一 个对 象 的这种 共 享是 不 同的 ,
因 为 这 种 情 况 a的 修 改 并 不 会 影 响 到 b .它 是 由 编 译 器 完 成 的 , 有 利 于 节 省 空 间 。 而 一 个 对 象 引 用 变 量 修 改 了 这 个 对 象 它 的 内部状 态 , 影 响 到另 一个 对象 引用 变量 。 会
间 。对象 通过 nw、e ary等 方式 建立 , 需要 程 序代 码来 显 e nw r a 不
第9 第7 卷 期
2 1 年 7月 00
软 件 导 刊
So t r i e fwa e Gu d
VO . 1 NHale Waihona Puke . 9 7 J . 01 u12 O
Jv a a虚 拟 机 内存 分 配 探 析
胡 雯
( 汉科 技 大 学 中南分校 , 北 武 汉 4 02 ) 武 湖 3 2 3
2 S r g 对 象 举 例 说 明 ti 类 n
Sr g是一 个特 殊 的包装 类 数据 。可 以用 : tn i
Sr gs = e t n (a c ) tn t nw Sr g ”b ” ; i r - i
S r g sr a c ; t n t i =” b ”
是 为 数 组 或 对 象 起 的 一 个 名 称 , 后 就 可 以 在 程 序 中 使 用 栈 中 以 的引用变 量来 访 问堆 中的数 组 或对 象 。 Jv aa是 自动 管 理 栈 和 堆 , 序 员 不 能 直 接 地 设 置 栈 或 堆 。 程 Jv aa的堆 是 一 个 运 行 时 数 据 存 储 区 , 的 对 象 从 中 分 配 空 类
内存分析工具MAT的使用

内存分析工具MAT的使用MAT(Memory Analyzer Tool)是一个开源的Java内存分析工具,可以帮助开发人员分析和优化Java应用程序的内存使用情况。
在开发和调试过程中,MAT可以帮助我们找到内存泄漏、大对象、重复对象以及内存使用高的地方,从而提高应用程序的性能和稳定性。
以下是使用MAT进行内存分析的一般步骤:2. 创建内存快照:在使用MAT进行内存分析之前,我们需要创建Java应用程序的内存快照。
可以通过以下几种方式创建内存快照:- 在Eclipse中启动的Java应用程序中,可以使用"Eclipse Memory Analyzer"菜单中的"Dump HPROF File"选项来创建内存快照。
- 在Java命令行应用程序中,可以通过添加JVM参数"-XX:+HeapDumpOnOutOfMemoryError"来在内存溢出错误发生时自动创建内存快照。
- 对于正在运行的Java进程,可以使用jmap命令手动创建内存快照。
3. 打开内存快照:启动MAT后,可以通过"File"菜单中的"Open Heap Dump"选项打开已经创建的内存快照。
MAT支持打开多种格式的内存快照文件,包括HPROF、IBM PHD、Eclipse MAT等。
4. 分析内存使用情况:在打开内存快照后,MAT会对内存进行解析和分析,并显示内存使用的概览信息。
在"Overview"选项卡中,我们可以查看内存占用的总大小、对象数量和最常见的对象类型。
5. 查找内存泄露:MAT提供了多种工具和视图,可以帮助我们找到内存泄漏的原因。
其中,"Leak Suspects"视图可以显示潜在的内存泄漏问题,"Path to GC Roots"视图可以展示垃圾回收根对象的引用链,帮助我们找到导致对象无法被回收的原因。
java内存使用情况的命令

java内存使用情况的命令Java是一种面向对象的编程语言,它在开发应用程序时需要使用内存来存储数据和执行代码。
因此,了解Java的内存使用情况对于开发人员来说是非常重要的。
Java虚拟机(JVM)负责管理Java应用程序的内存,它使用垃圾回收机制来自动管理内存的分配和释放。
JVM的内存可以分为以下几个部分:1. 堆(Heap):堆是Java程序运行时动态分配的内存区域,用于存储对象实例。
堆的大小可以通过命令行参数-Xmx和-Xms来设置。
-Xms表示JVM启动时初始分配的堆内存大小,-Xmx表示堆能够达到的最大内存大小。
2. 方法区(Method Area):方法区用于存储已加载的类信息、常量、静态变量等数据。
方法区的大小可以通过命令行参数-XX:PermSize和-XX:MaxPermSize来设置。
-XX:PermSize表示JVM启动时初始分配的方法区大小,-XX:MaxPermSize表示方法区能够达到的最大大小。
3. 栈(Stack):栈用于存储Java方法中的局部变量以及方法调用时的状态信息。
每个Java线程都有一个独立的栈,栈的大小是固定的,并且在线程创建时被分配。
栈的大小可以通过命令行参数-Xss来设置。
除了上述部分,JVM还会使用一些额外的内存空间,如直接内存(DirectMemory)和本地方法栈(Native Method Stack),用于存储一些特殊的数据和执行本地方法。
了解Java的内存使用情况对于定位内存泄漏和优化程序性能非常有帮助。
下面是几个常用的命令,可以用于监控和调整Java程序的内存使用情况:1. jps:该命令用于列出当前运行的Java进程,以及对应的进程ID。
2. jstat:该命令用于监控Java虚拟机的各种运行状态,包括堆的使用情况、类加载数量、垃圾回收情况等。
常用的参数包括-jstat -gcutil <pid>和-jstat-gccapacity <pid>。
java内存溢出排查方法解析

java内存溢出排查方法解析内存溢出(out of mem or y),通俗理解就是内存不够,通常在运行大型软件或游戏时,软件或游戏所需要的内存远远超出了你主机内安装的内存所承受大小,就叫内存溢出。
此时软件或游戏就运行不了,系统会提示内存溢出,有时候会自动关闭软件,重启电脑或者软件后释放掉一部分内存又可以正常运行该软件或游戏一段时间。
内存溢出已经是软件开发历史上存在了近40年的“老大难”问题,像在“红色代码”病毒事件中表现的那样,它已经成为黑客攻击企业网络的“罪魁祸首”。
如在一个域中输入的数据超过了它的要求就会引发数据溢出问题,多余的数据就可以作为指令在计算机上运行。
据有关安全小组称,操作系统中超过50%的安全漏洞都是由内存溢出引起的,其中大多数与微软的技术有关。
定义及原因内存溢出是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于虚拟机能提供的最大内存。
为了解决Java中内存溢出问题,我们首先必须了解Java是如何管理内存的。
Java的内存管理就是对象的分配和释放问题。
在Java中,内存的分配是由程序完成的,而内存的释放是由垃圾收集器(GarbageCollec ti on,GC)完成的,程序员不需要通过调用GC函数来释放内存,因为不同的JVM实现者可能使用不同的算法管理GC,有的是内存使用到达一定程度时,GC才开始工作,也有定时执行的,有的是中断式执行GC。
但GC只能回收无用并且不再被其它对象引用的那些对象所占用的空间。
Java的内存垃圾回收机制是从程序的主要运行对象开始检查引用链,当遍历一遍后发现没有被引用的孤立对象就作为垃圾回收。
1、内存溢出的原因是什么?内存溢出是由于没被引用的对象(垃圾)过多造成JVM没有及时回收,造成的内存溢出。
如果出现这种现象可行代码排查:一)是否App中的类中和引用变量过多使用了Stat ic修饰如publicst ai tc Student s;在类中的属性中使用 static修饰的最好只用基本类型或字符串。
Java内存分析工具jmap

Java内存分析⼯具jmap1.简述 jmap是⼀个多功能的命令,它可以⽣成java程序的dump⽂件,也可以查看堆内对象⽰例的统计信息、查看ClassLoader的信息以及finalizer队列。
2.jmap的⽤法 (1)jmap参数说明 参数说明:option:选项参数。
pid:需要打印配置信息的进程ID。
executable:产⽣核⼼dump的Java可执⾏⽂件。
core:需要打印配置信息的核⼼⽂件。
server-id:可选的唯⼀id,如果相同的远程主机上运⾏了多台调试服务器,⽤此选项参数标识服务器。
remote server IP or hostname:远程调试服务器的IP地址或主机名。
option选项参数说明:no option:查看进程的内存映像信息。
heap:显⽰Java堆详细信息。
histo[:live]:显⽰堆中对象的统计信息。
permstat:打印类加载器信息。
finalizerinfo:显⽰在F-Queue队列等待Finalizer线程执⾏finalizer⽅法的对象。
dump:<dump-options>:⽣成堆转储快照。
F:当-dump没有响应时,使⽤-dump或者-histo参数,在这个模式下,live⼦参数⽆效。
help:打印帮助信息。
J<flag>:指定传递给运⾏jmap的JVM的参数。
(2)no option⽰例 命令:jmap pid 描述:使⽤不带选项参数的jmap打印共享对象映射,将会打印⽬标虚拟机中加载的每个共享对象的起始地址、映射⼤⼩以及共享对象⽂件的路径全称。
这与Solaris的pmap⼯具⽐较相似。
(3)heap⽰例 命令:jmap -heap pid 描述:打印⼀个堆的摘要信息,包括使⽤的GC算法、堆配置信息和各内存区域内存使⽤信息。
C:\Users\Administrator>jmap -heap 1760Attaching to process ID 1760, please wait...Debugger attached successfully.Server compiler detected.JVM version is 24.80-b11using thread-local object allocation. #新⽣代采⽤的是并⾏线程处理⽅式Parallel GC with 4 thread(s) #同步并⾏垃圾回收Heap Configuration: #堆配置情况,也就是JVM参数配置的结果【平常说的tomcat配置JVM参数,就是在配置这些】MinHeapFreeRatio = 0 #最⼩堆使⽤⽐例MaxHeapFreeRatio = 100 #最⼤堆可⽤⽐例MaxHeapSize = 2147483648 (2048.0MB) #最⼤堆空间⼤⼩NewSize = 1310720 (1.25MB) #新⽣代分配⼤⼩MaxNewSize = 536870912 (512.0MB) #最⼤可新⽣代分配⼤⼩OldSize = 5439488 (5.1875MB) #⽼年代⼤⼩NewRatio = 2 #新⽣代⽐例SurvivorRatio = 8 #新⽣代与suvivor的⽐例PermSize = 21757952 (20.75MB) #perm区永久代⼤⼩MaxPermSize = 2147483648 (2048.0MB) #最⼤可分配perm区也就是永久代⼤⼩G1HeapRegionSize = 0 (0.0MB)Heap Usage: #堆使⽤情况【堆内存实际的使⽤情况】PS Young GenerationEden Space: #伊甸区capacity = 413663232 (394.5MB) #伊甸区容量used = 41933104 (39.99052429199219MB) #伊甸区已经使⽤⼤⼩free = 371730128 (354.5094757080078MB) #剩余容量10.137015029655814% used #伊甸区使⽤情况From Space: #survior1区capacity = 58195968 (55.5MB) #survior1区容量used = 0 (0.0MB) #surviror1区已使⽤情况free = 58195968 (55.5MB) #surviror1区剩余容量0.0% used #survior1区使⽤⽐例To Space: #survior2区capacity = 56623104 (54.0MB) #survior2区容量used = 0 (0.0MB) #survior2区已使⽤情况free = 56623104 (54.0MB) #survior2区剩余容量0.0% used #survior2区使⽤⽐例PS Old Generation #⽼年代使⽤情况capacity = 458227712 (437.0MB) #⽼年代容量used = 243170504 (231.90546417236328MB) #⽼年代已使⽤容量free = 215057208 (205.09453582763672MB) #⽼年代剩余容量53.0676119387559% used #⽼年代使⽤⽐例PS Perm Generation #永久代使⽤情况capacity = 176685056 (168.5MB) #perm区容量used = 92300856 (88.02495574951172MB) #perm区已使⽤容量free = 84384200 (80.47504425048828MB) #perm区剩余容量52.240329821668674% used #perm区使⽤⽐例62625 interned Strings occupying 7287440 bytes.View Code(4)histo[:live]⽰例 命令:jmap -histo:live pid 描述:包括每个Java类、对象数量、内存⼤⼩(单位:字节)、完全限定的类名。
Java内存泄漏分析系列之一:使用jstack定位线程堆栈信息
Java内存泄漏分析系列之⼀:使⽤jstack定位线程堆栈信息原⽂地址:前⼀段时间上线的系统升级之后,出现了严重的⾼CPU的问题,于是开始了⼀系列的优化处理之中,现在将这个过程做成⼀个系列的⽂章。
基本概念在对Java内存泄漏进⾏分析的时候,需要对jvm运⾏期间的内存占⽤、线程执⾏等情况进⾏记录的dump⽂件,常⽤的主要有thread dump和heap dump。
thread dump 主要记录JVM在某⼀时刻各个线程执⾏的情况,以栈的形式显⽰,是⼀个⽂本⽂件。
通过对thread dump⽂件可以分析出程序的问题出现在什么地⽅,从⽽定位具体的代码然后进⾏修正。
thread dump需要结合占⽤系统资源的线程id进⾏分析才有意义。
heap dump 主要记录了在某⼀时刻JVM堆中对象使⽤的情况,即某个时刻JVM堆的快照,是⼀个⼆进制⽂件,主要⽤于分析哪些对象占⽤了太对的堆空间,从⽽发现导致内存泄漏的对象。
上⾯两种dump⽂件都具有实时性,因此需要在服务器出现问题的时候⽣成,并且多⽣成⼏个⽂件,⽅便进⾏对⽐分析。
下⾯我们先来说⼀下如何⽣成 thread dump。
使⽤⽣成thread dump当服务器出现⾼CPU的时候,⾸先执⾏top -c命令动态显⽰进程及占⽤资源的排⾏,如下图:top后⾯的参数-c可以显⽰进程详细的信息。
top命令执⾏的时候还可以执⾏⼀些快捷键:1对于多核服务器,可以显⽰各个CPU占⽤资源的情况shift+h显⽰所有的线程信息shift+w将当前top命令的设置保存到~/.toprc⽂件中,这样不⽤每次都执⾏快捷键了以上图为例,pid为1503的进程占⽤了⼤量的CPU资源,接下来需要将占⽤CPU最⾼进程中的线程打印出来,可以⽤top -bn1 -H -p <pid>命令,执⾏结果如下:上⾯-bn1参数的含义是只输出⼀次结果,⽽不是显⽰⼀个动态的结果。
我个⼈请喜欢⽤ps -mp <pid> -o THREAD,tid,time | sort -k2r命令查看,后⾯的sort参数根据线程占⽤的cpu⽐例进⾏排序,结果如下:接下来我们清楚今天的主⾓jstack,这是⼀个在JDK5开始提供的内置⼯具,可以打印指定进程中线程运⾏的状态,包括线程数量、是否存在死锁、资源竞争情况和线程的状态等等。
memoryanalyzer 用法
一、什么是memoryanalyzer?Memoryanalyzer 是一个用于分析 Java 程序在运行过程中的内存使用情况的工具。
它可以帮助开发人员识别和解决内存泄漏和内存溢出等问题,以提高程序的性能和稳定性。
二、 memoryanalyzer 的主要功能1. 内存快照分析:通过获取 Java 程序的内存快照,memoryanalyzer 可以帮助开发人员查看程序在不同时间点的内存使用情况,以便分析内存泄漏和内存溢出等问题。
2. 内存泄漏检测:memoryanalyzer 可以帮助开发人员识别程序中存在的内存泄漏问题,从而及时解决这些问题,提高程序的内存利用率和性能。
3. 内存溢出分析:通过分析程序在运行过程中出现的内存溢出情况,memoryanalyzer 可以帮助开发人员定位问题代码,以便进行优化和改进。
三、 memoryanalyzer 的使用方法1. 获取内存快照:使用 memoryanalyzer 工具可以获取 Java 程序的内存快照。
可以使用命令行工具或者图形界面工具来获取内存快照。
2. 分析内存快照:获取内存快照后,可以使用 memoryanalyzer 工具来对内存快照进行分析,以查看程序在不同时间点的内存使用情况,并识别可能存在的内存泄漏和内存溢出问题。
3. 解决内存问题:根据 memoryanalyzer 的分析结果,开发人员可以及时解决程序中存在的内存问题,从而提高程序的性能和稳定性。
四、 memoryanalyzer 的优点和局限性1. 优点:memoryanalyzer 可以帮助开发人员快速定位并解决程序中的内存问题,提高程序的性能和稳定性。
2. 局限性:memoryanalyzer 在使用过程中需要一定的学习成本,而且对于庞大的内存快照的分析可能会消耗较多的时间和计算资源。
五、总结通过以上的介绍,我们可以看到,memoryanalyzer 是一个功能强大的工具,可以帮助开发人员分析 Java 程序的内存使用情况,并解决程序中可能存在的内存问题。
对于LINUX类主机JAVA应用程序占用CPU内存过高的分析方法介绍
对于LINUX类主机JAVA应用程序占用CPU内存过高的分析方法介绍在Linux类主机上运行的Java应用程序占用过高的CPU或内存可能是由于多种原因引起的,例如代码问题、配置问题、资源不足等。
为了解决这些问题,可以采取以下分析方法:1. 监控系统资源:首先,需要使用系统监控工具,如top、htop、sar等,来监控CPU利用率、内存使用情况、磁盘IO等系统资源。
通过观察系统资源的变化,确定是否存在CPU或内存资源过高的问题。
2. 分析Java程序:使用Java的诊断工具,如jmap、jstack、jvisualvm等,来分析Java应用程序。
通过这些工具可以获取到程序的堆内存使用情况、线程堆栈信息等,从而查找可能导致内存泄漏或者线程阻塞的问题。
3. 检查系统配置:检查Java虚拟机(JVM)的启动参数和堆内存设置。
虚拟机参数如-Xmx和-Xms用于指定最大堆内存和初始堆内存的大小。
如果堆内存设置过小,可能导致频繁的垃圾回收,从而造成CPU占用过高的问题。
可以通过调整这些参数来优化内存的使用。
4. 垃圾回收分析:通过使用Java的垃圾回收日志分析工具,如GCViewer,可以分析垃圾回收过程中的各项指标,如垃圾收集时间、吞吐量等。
通过分析垃圾回收的情况,可以确定是否存在内存泄漏或者频繁的Full GC现象,从而优化垃圾回收策略。
5. 代码调优:如果发现CPU占用高的问题,可以使用Java的性能分析工具,如jprofiler、YourKit等,来分析程序的热点代码,找出占用CPU资源较多的部分。
然后可以针对这些热点代码进行优化,如改进算法、减少循环次数、使用缓存等。
6.分析外部资源:除了CPU和内存资源外,还需要分析应用程序所使用的其他外部资源,如数据库连接、网络请求等。
可以使用性能分析工具来分析这些外部资源的使用情况,确定是否存在资源泄漏或者资源竞争的问题。
7.优化系统结构:如果经过以上步骤仍然无法解决CPU或内存占用过高的问题,可能需要考虑重新设计系统架构。
java常见的内存资源分析、性能分析工具有哪些?
VisualVM是一个资源分析工具,一直从JDK 6更新到7,它默认内存和CPU的监视,它可以告诉你哪个类和方法消耗资源,但它不会显示代码流程。
2.JProfiler
JProfiler很容易安装,并且通过向导,你可以选择应用服务器用来运行应用程序。我不得不选择使用JPofiler应用服务器的主目录,以及向导生成的一个单独的启动脚本。然后运行服务器。在监听会话的过程中,它会提供几个选项,它可以记录内存的使用和CPU的使用率。在查看CPU使用率的同时,可以看到执行路径。这让我看到应用程序大部分时间都花在请求上。我们可以将IntelliJ插件安装到IDE上,那么运行JProfiler会更加便利。比如,可以直接帮我启动Tomcat。
我刚开始创建JProbe时遇到点困难。安装并不是直接完成,我需要对它进行配置。它采用了类似JProfiler一样的设置。它会在你的Tomcat目录中生成启动脚本,可以通过脚本启动服务器并监听会话。它的界面包含有按钮和表格,其中可以看到内存的使用,但无法在进程中找到执行路径。 5来自Spring Insight
结 论
如果你有基于Spring的应用程序,那么选择Spring Insight显然是最佳的。它一直是免费的,但你需要部署你的应用在TC Server上。
如果你想监听本地和远程的进程,我会选择JProfiler或YourKit.这两个也可以帮助Spring Insight来监测、找到性能瓶颈。
JProfiler和YourKit已经能满足我的上述要求。JProfiler、YourKit和Spring Insight都可以跟踪应用程序的类和方法的流向。JProfiler和YourKit可以显示内存使用情况。虽然Spring Insight不可以显示内存使用情况,但它可以很好的显示吞吐量的趋势。这三款性能分析工具功能很清楚,不混乱,而且容易使用。最后,他们都有自己的 IDE插件。希望本文能帮助你做出正确的选择。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
1 JVM简介JVM是我们Javaer的最基本功底了,刚开始学Java的时候,一般都是从“Hello World”开始的,然后会写个复杂点class,然后再找一些开源框架,比如Spring,Hibernate等等,再然后就开发企业级的应用,比如网站、企业内部应用、实时交易系统等等,直到某一天突然发现做的系统咋就这么慢呢,而且时不时还来个内存溢出什么的,今天是交易系统报了StackOverflowError,明天是网站系统报了个OutOfMemoryError,这种错误又很难重现,只有分析Javacore和dump 文件,运气好点还能分析出个结果,运行遭的点,就直接去庙里烧香吧!每天接客户的电话都是战战兢兢的,生怕再出什么幺蛾子了。
我想Java做的久一点的都有这样的经历,那这些问题的最终根结是在哪呢?—— JVM。
JVM全称是Java Virtual Machine,Java虚拟机,也就是在计算机上再虚拟一个计算机,这和我们使用 VMWare不一样,那个虚拟的东西你是可以看到的,这个JVM你是看不到的,它存在内存中。
我们知道计算机的基本构成是:运算器、控制器、存储器、输入和输出设备,那这个JVM也是有这成套的元素,运算器是当然是交给硬件CPU还处理了,只是为了适应“一次编译,随处运行”的情况,需要做一个翻译动作,于是就用了JVM自己的命令集,这与汇编的命令集有点类似,每一种汇编命令集针对一个系列的CPU,比如8086系列的汇编也是可以用在8088上的,但是就不能跑在8051上,而JVM的命令集则是可以到处运行的,因为JVM做了翻译,根据不同的CPU,翻译成不同的机器语言。
JVM中我们最需要深入理解的就是它的存储部分,存储?硬盘?NO,NO, JVM是一个内存中的虚拟机,那它的存储就是内存了,我们写的所有类、常量、变量、方法都在内存中,这决定着我们程序运行的是否健壮、是否高效,接下来的部分就是重点介绍之。
2 JVM的组成部分我们先把JVM这个虚拟机画出来,如下图所示:从这个图中可以看到,JVM是运行在操作系统之上的,它与硬件没有直接的交互。
我们再来看下JVM有哪些组成部分,如下图所示:该图参考了网上广为流传的JVM构成图,大家看这个图,整个JVM分为四部分: Class Loader 类加载器类加载器的作用是加载类文件到内存,比如编写一个HelloWord.java程序,然后通过javac编译成class文件,那怎么才能加载到内存中被执行呢?Class Loader承担的就是这个责任,那不可能随便建立一个.class文件就能被加载的,Class Loader加载的class文件是有格式要求,在《JVM Specification》中式这样定义Class文件的结构:ClassFile {u4 magic;u2 minor_version;u2 major_version;u2 constant_pool_count;cp_info constant_pool[constant_pool_count-1];u2 access_flags;u2 this_class;u2 super_class;u2 interfaces_count;u2 interfaces[interfaces_count];u2 fields_count;field_info fields[fields_count];u2 methods_count;method_info methods[methods_count];u2 attributes_count;attribute_info attributes[attributes_count];}需要详细了解的话,可以仔细阅读《JVM Specification》的第四章“The class File Format”,这里不再详细说明。
友情提示:Class Loader只管加载,只要符合文件结构就加载,至于说能不能运行,则不是它负责的,那是由Execution Engine负责的。
❑∙ Execution Engine 执行引擎执行引擎也叫做解释器(Interpreter),负责解释命令,提交操作系统执行。
❑∙ Native Interface本地接口本地接口的作用是融合不同的编程语言为Java所用,它的初衷是融合C/C++程序,Java诞生的时候是C/C++横行的时候,要想立足,必须有一个聪明的、睿智的调用C/C++程序,于是就在内存中专门开辟了一块区域处理标记为native的代码,它的具体做法是Native Method Stack中登记native方法,在Execution Engine执行时加载native libraies。
目前该方法使用的是越来越少了,除非是与硬件有关的应用,比如通过Java程序驱动打印机,或者Java系统管理生产设备,在企业级应用中已经比较少见,因为现在的异构领域间的通信很发达,比如可以使用Socket通信,也可以使用Web Service等等,不多做介绍。
❑∙ Runtime data area运行数据区运行数据区是整个JVM的重点。
我们所有写的程序都被加载到这里,之后才开始运行,Java生态系统如此的繁荣,得益于该区域的优良自治,下一章节详细介绍之。
整个JVM框架由加载器加载文件,然后执行器在内存中处理数据,需要与异构系统交互是可以通过本地接口进行,瞧,一个完整的系统诞生了!2 JVM的内存管理所有的数据和程序都是在运行数据区存放,它包括以下几部分:Stack 栈栈也叫栈内存,是Java程序的运行区,是在线程创建时创建,它的生命期是跟随线程的生命期,线程结束栈内存也就释放,对于栈来说不存在垃圾回收问题,只要线程一结束,该栈就Over。
问题出来了:栈中存的是那些数据呢?又什么是格式呢?栈中的数据都是以栈帧(Stack Frame)的格式存在,栈帧是一个内存区块,是一个数据集,是一个有关方法(Method)和运行期数据的数据集,当一个方法A被调用时就产生了一个栈帧F1,并被压入到栈中,A方法又调用了B方法,于是产生栈帧F2也被压入栈,执行完毕后,先弹出F2栈帧,再弹出F1栈帧,遵循“先进后出”原则。
那栈帧中到底存在着什么数据呢?栈帧中主要保存3类数据:本地变量(Local Variables),包括输入参数和输出参数以及方法内的变量;栈操作(Operand Stack),记录出栈、入栈的操作;栈帧数据(Frame Data),包括类文件、方法等等。
光说比较枯燥,我们画个图来理解一下Java栈,如下图所示:图示在一个栈中有两个栈帧,栈帧2是最先被调用的方法,先入栈,然后方法2又调用了方法1,栈帧1处于栈顶的位置,栈帧2处于栈底,执行完毕后,依次弹出栈帧1和栈帧2,线程结束,栈释放。
Heap 堆内存一个JVM实例只存在一个堆类存,堆内存的大小是可以调节的。
类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,以方便执行器执行,堆内存分为三部分:Permanent Space 永久存储区永久存储区是一个常驻内存区域,用于存放JDK自身所携带的Class,Interface 的元数据,也就是说它存储的是运行环境必须的类信息,被装载进此区域的数据是不会被垃圾回收器回收掉的,关闭JVM才会释放此区域所占用的内存。
Young Generation Space 新生区新生区是类的诞生、成长、消亡的区域,一个类在这里产生,应用,最后被垃圾回收器收集,结束生命。
新生区又分为两部分:伊甸区(Eden space)和幸存者区(Survivor pace),所有的类都是在伊甸区被new出来的。
幸存区有两个: 0区(Survivor 0 space)和1区(Survivor 1 space)。
当伊甸园的空间用完时,程序又需要创建对象,JVM的垃圾回收器将对伊甸园区进行垃圾回收,将伊甸园区中的不再被其他对象所引用的对象进行销毁。
然后将伊甸园中的剩余对象移动到幸存0区。
若幸存0区也满了,再对该区进行垃圾回收,然后移动到1区。
那如果1区也满了呢?再移动到养老区。
Tenure generation space养老区养老区用于保存从新生区筛选出来的JAVA对象,一般池对象都在这个区域活跃。
三个区的示意图如下:❑∙ Method Area 方法区方法区是被所有线程共享,该区域保存所有字段和方法字节码,以及一些特殊方法如构造函数,接口代码也在此定义。
❑∙ PC Register 程序计数器每个线程都有一个程序计数器,就是一个指针,指向方法区中的方法字节码,由执行引擎读取下一条指令。
❑∙ Native Method Stack 本地方法栈3 JVM相关问题问:堆和栈有什么区别答:堆是存放对象的,但是对象内的临时变量是存在栈内存中,如例子中的methodVar是在运行期存放到栈中的。
栈是跟随线程的,有线程就有栈,堆是跟随JVM的,有JVM就有堆内存。
问:堆内存中到底存在着什么东西?答:对象,包括对象变量以及对象方法。
问:类变量和实例变量有什么区别?答:静态变量是类变量,非静态变量是实例变量,直白的说,有static修饰的变量是静态变量,没有static修饰的变量是实例变量。
静态变量存在方法区中,实例变量存在堆内存中。
问:我听说类变量是在JVM启动时就初始化好的,和你这说的不同呀!答:那你是道听途说,信我的,没错。
问:Java的方法(函数)到底是传值还是传址?答:都不是,是以传值的方式传递地址,具体的说原生数据类型传递的值,引用类型传递的地址。
对于原始数据类型,JVM的处理方法是从Method Area或Heap 中拷贝到Stack,然后运行frame中的方法,运行完毕后再把变量指拷贝回去。
问:为什么会产生OutOfMemory产生?答:一句话:Heap内存中没有足够的可用内存了。
这句话要好好理解,不是说Heap没有内存了,是说新申请内存的对象大于Heap空闲内存,比如现在Heap 还空闲1M,但是新申请的内存需要1.1M,于是就会报OutOfMemory了,可能以后的对象申请的内存都只要0.9M,于是就只出现一次OutOfMemory,GC也正常了,看起来像偶发事件,就是这么回事。
但如果此时GC没有回收就会产生挂起情况,系统不响应了。
问:我产生的对象不多呀,为什么还会产生OutOfMemory?答:你继承层次忒多了,Heap中产生的对象是先产生父类,然后才产生子类,明白不?问:OutOfMemory错误分几种?答:分两种,分别是“OutOfMemoryError:java heap size”和”OutOfMemoryError: PermGen space”,两种都是内存溢出,heap size是说申请不到新的内存了,这个很常见,检查应用或调整堆内存大小。