欢迎光临散文网 会员登陆 & 注册

还不懂JVM内存管理?

2020-12-18 11:15 作者:编程大战  | 我要投稿

一、物理内存与虚拟内存

物理内存RAM(随机存储器),寄存单元为寄存器,用于存储计算单元执行指令的中间结果。

连接处理器和RAM或者处理器和寄存器的是地址总线,这个地址的宽度影响了物理地址的索引范围,总线的宽度决定了处理器一次可以从寄存器或者内存中获取多少个bit。

虚拟内存的出现使不同进程在同时运行时可以共享物理内存,提高内存利用率,而且能扩展内存的地址空间。

二、内核空间与用户空间

一个电脑4GB的地址空间被划分为内核空间和用户空间,程序只能使用用户空间的内存。

内核空间主要是指操作系统运行时所使用的用于程序调度、虚拟内存的使用或者连接硬件资源等的程序逻辑。

三、Java中那些组件需要使用内存

Java堆

Java堆是用于存储Java对象的内存区域,堆的大小在JVM启动时就一次向操作系统申请完成,通过-Xmx(最大)和-Xms(初始)两个选项来控制大小,一旦分配完成后就固定了。Java堆中内存空间的管理由JVM来控制,对象创建由Java应用程序控制,对象所占的空间释放由管理内存的垃圾收集器来完成。

线程

JVM运行实际程序的实体是线程,而线程需要内存空间来存储必要的数据。每个线程创建时JVM都会为它创建一个堆栈。

类和类的加载器

Java中类和加载类本身同样需要存储空间,这个区域叫永久带(PermGen区)。

卸载类(内存回收)条件:

1、Java堆中没有对表示该加载器的java.lang.ClassLoader对象引用

2、Java堆没有对表示类加载器的类的任何java.lang.Class对象的引用

3、在Java堆上该类加载器加载的任何类的所有对象都不在存活(被引用)

NIO

NIO使用java.nio.ByteBuffer.allocateDirect()方法分配内存,是本机内存而不是Java堆上的内存,增加了一次系统调用。直接ByteBuffer产生的数据和网络或者磁盘交互都在操作系统的内核空间中发生,不需要将数据复制到Java内存中,加快数据处理速度。

JNI

JNI技术使得本机代码(C语言)可以调用Java方法,即native memory

四、JVM内存结构

在Java虚拟机规范中将Java运行时数据划分为6种

PC寄存器数据

保存当前执行的程序的内存地址

Java栈

Java栈总是和线程关联,每当创建一个线程时,JVM就会为这个线程创建一个对应的Java栈,这个栈中又会含有多个栈帧,这些栈帧是与每个方法关联起来,每运行一个方法就创建一个栈帧,每一个栈帧会含有一些内存变量,操作栈和方法返回值等信息。

堆是存放Java对象的地方,是JVM管理Java对象的核心内存区域,每个存储在堆中的Java对象都是这个对象的类的一个副本,它会复制包括继承自它父类的所有非静态属性

方法区

JVM方法区用于存储类结构信息的地方,在Java堆中的永久区内,在启动程序后一段时间就固定饿了

本地方法栈

是为JVM运行Native方法准备的空间,由于很多Native方法是C语言实现的,所以也叫C栈

运行时常量池

代表运行时每个class文件中的常量表

五、JVM内存结构

在Java虚拟机规范中将Java运行时数据划分为6种

PC寄存器数据

保存当前执行的程序的内存地址

Java栈

Java栈总是和线程关联,每当创建一个线程时,JVM就会为这个线程创建一个对应的Java栈,这个栈中又会含有多个栈帧,这些栈帧是与每个方法关联起来,每运行一个方法就创建一个栈帧,每一个栈帧会含有一些内存变量,操作栈和方法返回值等信息。

堆是存放Java对象的地方,是JVM管理Java对象的核心内存区域,每个存储在堆中的Java对象都是这个对象的类的一个副本,它会复制包括继承自它父类的所有非静态属性

方法区

JVM方法区用于存储类结构信息的地方,在Java堆中的永久区内,在启动程序后一段时间就固定饿了

本地方法栈

是为JVM运行Native方法准备的空间,由于很多Native方法是C语言实现的,所以也叫C栈

运行时常量池

代表运行时每个class文件中的常量表

六、JVM内存回收策略

静态内存分配和回收

Java中静态内存分配指在Java被编译时就已经能够确定需要的内存空间,当程序加载时系统把内存一次性分配给它。这些内存只有在程序结束时才被收回。

动态内存分配和回收

Java中对象的内存空间是动态分配的,就是在程序执行时才知道要分配的存储空间大小,只有等到对象不再使用时才会被回收。

如何检测垃圾

只要某个对象不再被其他活动对象引用,那么这个对象就可以被回收。活动对象指能够被一个根对象集合到达的对象。

根对象集合:

  • 1、方法中局部变量区中对象的引用

  • 2、Java操作栈中的对象引用

  • 3、常量池中对象引用

  • 4、本地方法中持有的对象引用

  • 5、类的Class对象

基于分代的垃圾收集算法

  1. Young区 分为Eden区和两个Survivor区,其中新创建的对象都在Eden区,当Eden区满后触发minor GC 将Eden区仍然存活的对象复制到Survivor区中,另外一个Survivor区中的存活对象也复制到这个Survivor中,保证始终有一个Survivor区是空的。

  2. Old区存放的是Young区的Survivor满后触发minno GC后仍然存活的对象,当Eden区满后将对象存放到Survivor区中,如果Survivor中仍然存放不下这些对象,GC收集器会将这些对象直接存放到Old区。如果Survivor区中对象足够老,也直接存放到Old区。如果Old区也满了,将会触发Full GC回收整个堆内存。

  3. Perm区存放的主要是类的Class对象,如果一个类被频繁地加载,也可能会导致Perm区满,Perm区的垃圾回收也是Full GC触发的。

三类垃圾收集算法:

1、Serial Collector

是JVM在client模式下的默认的GC方式,通过JVM配置参数-XX:+UseSerialGC来指定GC使用该手机算法。当Eden空间不足时就触发Minor GC,触发Minor GC时首先检查之前每次Minor GC时晋升到Old区的平均对象大小是否大于Old区的剩余空间,如果大于,则直接触发Full GC,如果小于,则看HandlePromotionFailur参数的值。如果为true,仅触发Minor GC,否则再触发一次Full GC。

Java参数:java -Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails

2、Parallel Collector

Parallel GC根据Minor GC 和Full GC的不同分为三种,分别是ParNewGC、ParallelGC和ParallelOldGC。

1)ParNewGC:通过-XX:+UseParNewGC参数来指定,对象分配和回收策略与Serial Collector类似,只是回收的线程是多线程的。

2)ParallelGC:Server下默认的GC方式,当在Eden区申请内存空间时,如果Eden区不够,那么看当前申请的空间是否大于等于Eden的一半,如果大于则这次申请的空间直接在Old中分配,小于则触发Minor GC。在触发GC之前首先会检查每次晋升到Old区的平均大小是否大于Old区的剩余空间,如果大于则再出发Full GC。在这次触发GC后仍然会按照这个规则重新检查一次。

JVM参数:java -Xms20M -Xmx20M -Xmn10M -XX:+UsePaallelGC -XX:+PrintGCDetails

3)ParallelOldGC

和ParallelGC的区别:前者Full GC进行的动作为清空整个Heap堆中的垃圾对象,清楚Perm区中已经被卸载的类信息,并进行压缩。而后者是清楚Heap堆中部分垃圾对象,并进行部分的空间压缩。

3、CMS Collector

触发规则:检查Old区或者Perm区的使用率,当达到一定比例时触发CMS GC,触发时会回收Old区中的内存空间。触发Full GC:1)Eden分配失败,Minor GC后分配到To Space,To

Space不够再分配到Old区,Old区不够再出发Full GC 2)当CMS GC正在进行时向Old申请内存失败则会直接触发Full GC。

4、三种GC优缺点对比

七、内存问题分析

GC日志分析

<collector>GC 表示收集器的名称

<starting occupancy1>表示Young区在GC前占用的内存。

<ending occupancy1>表示Young区在GC后占用的内存。

<pause time1>表示Young区局部收集时JVM暂停处理的时间。

<starting occupany2>表示JVM Heap在GC前占用的内存

<ending occupany2>表示JVM Heap在GC后占用的内存

<pause time2>表示GC过程中JVM暂停处理的总时间

根据日志判断是否存在内存泄漏,

  • 如果<ending occupancy1>-<starting occupany1>=<ending occupancy2>-<starting occupany2>,则表明这次GC对象100%被回收,没有对象进入Old区或者Perm区。

  • 如果是大于号则这次回收对象进入Old区或者Perm区。如果<ending occupany2>一直增长,而且Full GC很频繁,则可能内存泄漏。

需要更多java学习资料的小伙伴可以在评论区回复“666”~




还不懂JVM内存管理?的评论 (共 条)

分享到微博请遵守国家法律