1、JVM内存模型划分
程序计数器(Program Counter Register)、虚拟机栈(VM Stack)、本地方法栈(Native Method Stack)、方法区(Method Area)、堆(Heap)。
2、内存模型介绍
线程私有区域:程序计数器、Java虚拟机栈、本地方法栈
线程共享区域:Java堆、方法区、运行时常量池
1.程序计数器(线程私有)
程序计数器是一块比较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器。
如果当前线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行 的是一个Native方法,这个计数器值为空。
2.Java虚拟机栈(线程私有)
虚拟机栈描述的是Java方法执行的内存模型 : 每个方法执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
每一个方法从调用直至执行完成的过程,就对应一个栈帧在虚拟机栈中入栈和出栈 的过程。
生命周期与线程相同:在创建线程时同时创建此线程的虚拟机栈,线程执行结束,虚拟机栈与线程一同被回收。\
此区域一共会产生以下两种异常:
如果线程请求的栈深度大于虚拟机所允许的深度(-Xss设置栈容量),将会抛出StackOverFlowError异常。
虚拟机在动态扩展时无法申请到足够的内存,会抛出OOM(OutOfMemoryError)异常\
OutOfMemory\
堆内存溢出
java堆用于存放对象的实例,当需要为对象的实例分配内存时,而堆的占用已经达到了设置的最大值(通过-Xmx)设置最大值,则抛出OutOfMemoryError异常。 栈内存溢出 java程序启动一个新线程时,没有足够的空间为该线程分配java栈,一个线程java栈的大小由-Xss设置决定;JVM则抛出OutOfMemoryError异常。方法区内存溢出
方法区用于存放java类的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。在类加载器加载class文件到内存中的时候,JVM会提取其中的类信息,并将这些类信息放到方法区中。
当需要存储这些类信息,而方法区的内存占用又已经达到最大值(通过-XX:MaxPermSize);将会抛出OutOfMemoryError异常。
对于这种情况的测试,基本的思路是运行时产生大量的类去填满方法区,直到溢出。这里需要借助CGLib直接操作字节码运行时,生成了大量的动态类
3.本地方法栈(线程私有)
本地方法栈与虚拟机栈的作用完全一样,他俩的区别是本地方法栈为虚拟机使用的Native方法服务,而虚拟机栈为JVM执行的Java方法服务。
在HotSpot虚拟机中,本地方法栈与虚拟机栈是同一块内存区域。
- Java堆(线程共享)
线程共享内存:所有线程共享此内存空间,此空间对所有线程可见
Java堆(Java Heap)是JVM所管理的大内存区域。
Java堆是所有线程共享的一块区域,在JVM启动时创建。
此内存区域存放的都是对象实例。\
JVM规范中说到:所有的对象实例以及数组都要在堆上分配。\
Java堆是垃圾回收器管理的最主要的内存区域;且Java堆可以处于物理上不连续的内存空间。
Java堆在主流的虚拟机中都是可扩展的(-Xmx设置大值,-Xms设置小值)。
若在堆中没有足够的内存完成对象实例分配并且堆无法再次扩展时,抛出OOM异常。
OOM:
1)内存溢出:内存中的对象确实还应该存活,但由于内存不够用产生的异常。
2)内存泄漏:对象无法被GC(垃圾对象无法被回收)
5.方法区(线程共享)
方法区与Java堆一样,是各个线程共享的内存区域。
它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据;\
JDK8之前称为永久代;
JDK8之后称为元空间(Meta Space)
元空间是堆外空间。元空间并不在虚拟机中,而是使用本地内存。默认情况下,元空间的大小仅受本地内存限制
3、JVM调优
3.1 JVM调优目的
减少GC的频率,通过减少Minor GC和减少Full GC的次数(将进入老年代的对象数量降到最低)来实现\
3.2 Minor Gc和Full GC区别
Minor GC也称为新生代GC,它是指发生在新生代内存区域的垃圾回收。
新生代是Java虚拟机中划分的内存区域,通常用于存放新创建的对象。
主要作用是回收新生代中的垃圾对象。
在执行Minor GC时,虚拟机会将新生代内存区域分为一个较大的Eden空间和两个较小的Survivor空间。
当Eden空间内存满时,虚拟机会将其中存活的对象复制到其中一个Survivor空间中,然后清空Eden空间。
当一个Survivor空间内存满时,虚拟机会将其中存活的对象复制到另一个Survivor空间中,同时清空原来的Survivor空间。
经过多次复制和清空操作,存活时间较长的对象会被移到老年代中,而非常年轻的对象则会被回收。(采用复制算法)
Full GC也称为老年代GC,它是指发生在老年代内存区域的垃圾回收。
老年代是Java虚拟机中划分的内存区域,通常用于存放较长时间存活的对象。
Full GC的主要作用是回收老年代中的垃圾对象。
在执行Full GC时,虚拟机会清理整个堆内存区域,包括新生代和老年代。
Full GC通常需要耗费更长的时间,因为它需要对整个堆内存区域进行垃圾回收操作,而且可能会触发多次Minor GC。(标记-整理算法)
总体来说,Minor GC和Full GC是不同的垃圾回收方式,Minor GC通常比Full GC更频繁,但耗费的时间更短。
同时,Full GC会涉及到整个堆内存区域的垃圾回收,因此对系统的性能和响应时间会有较大的影响,应尽量避免频繁触发
3.3JVM调优的措施
1、jps:虚拟机进程状况\
jps-l :正在运行的Java应用程序的进程ID和应用程序的main类名
jps-v :Java虚拟机的进程ID、状态、启动类名、堆栈信息以及命令行参数等详细信息\
jps-l
jps-v
2、jstat:虚拟机统计信息监视工具
命令格式
jstat -gc : 可以显示gc的信息,查看gc的次数,及时间。
具体描述
S0C 年轻代中第一个survivor(幸存区)的容量 (字节)
S1C 年轻代中第二个survivor(幸存区)的容量 (字节)
S0U 年轻代中第一个survivor(幸存区)目前已使用空间 (字节)
S1U 年轻代中第二个survivor(幸存区)目前已使用空间 (字节)
EC 年轻代中Eden(伊甸园)的容量 (字节)
EU 年轻代中Eden(伊甸园)目前已使用空间 (字节)
OC Old代的容量 (字节)
OU Old代目前已使用空间 (字节)
PC Perm(持久代)的容量 (字节)
PU Perm(持久代)目前已使用空间 (字节)
YGC 从应用程序启动到采样时年轻代中gc次数
YGCT 从应用程序启动到采样时年轻代中gc所用时间(s)
FGC 从应用程序启动到采样时old代(全gc)gc次数
FGCT 从应用程序启动到采样时old代(全gc)gc所用时间(s)
GCT 从应用程序启动到采样时gc用的总时间(s)$
3、jmap:Java内存映像工具 jmap 用于生成 heap dump 文件。
1、实践如下:
jps -l ||ps -ef | grep java查看java进程
2、生成dump文件
jmap -dump:live,format=b,file=/home/app/sxpservice/temp/heapdump.hprof 26196
然后使用jvisualvm导入文件
还可以用jvisualvm自动检测死锁
远程连接jvisualvm:启动普通的jar程序JMX端口配置
java -Dcom.sun.management.jmxremote.port=8888 -Djava.rmi.server.hostname=192.168.. -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -jar cogo-collect-web-0.0.8-prod-SNAPSHOT.jar
1. 使用top命令
top命令是一个实时的系统监控工具,可以显示系统中各个进程的CPU使用率。在终端中输入top命令后,会显示一个动态更新的进程列表,其中包括每个进程的CPU使用率、内存占用等信息。按下键盘上的1键可以显示每个CPU核心的使用情况。
- 使用htop命令
htop是top命令的增强版,提供了更友好的界面和更多的功能。与top命令相比,htop可以直接显示每个CPU核心的使用率,而不需要按下键盘上的1键。在终端中输入htop命令后,会打开一个交互式的进程列表,可以方便地查看和管理进程。
- 使用sar命令
sar命令是系统性能分析工具,可以用来收集和报告系统的各种性能数据,包括CPU使用率。在终端中输入sar命令后,可以指定参数来获取特定时间段内的CPU使用率数据。例如,sar -u命令可以获取CPU使用率的统计信息。
- 使用mpstat命令
mpstat命令是多核CPU性能分析工具,可以显示每个CPU核心的使用率。在终端中输入mpstat命令后,会显示每个CPU核心的平均使用率以及各个核心的详细使用情况。
- 使用pidstat命令
pidstat命令可以用来监控指定进程的CPU使用率。在终端中输入pidstat命令后,可以指定参数来监控特定进程的CPU使用率。例如,pidstat -p <进程ID>命令可以监控指定进程的CPU使用率。