JVM类加载机制

  .java文件编译—>生成JVM能够识别的.class字节码文件—>JVM把.class文件加载到内存—>对数据进行校验、转换解析、初始化

后续由执行引擎执行,在执行过程中,需要运行时数据区提供数据

  类的生命周期是加载、验证、准备、解析、初始化、使用、卸载

JVM内存管理机制

  JVM将内存划分为几个部分:PC寄存器(程序计数器)、堆、虚拟机栈、本地方法栈、方法区

*
PC寄存器(程序计数器):用于记录当前线程运行时的位置,每一个线程都有一个独立的程序计数器,线程的阻塞、恢复、挂起等一系列操作都需要程序计数器的参与,因此必须是线程私有的。
* 堆:java堆被所有线程共享,堆的主要作用就是存储对象。如果堆空间不够,但扩展时又不能申请到足够的内存时,则抛出OutOfMemoryError异常。
*
虚拟机栈:在创建线程时创建的,用来存储栈帧,因此也是线程私有的。java程序中的方法在执行时,会创建一个栈帧,用于存储方法运行时的临时数据和中间结果,包括局部变量表、操作数栈、动态链接、方法出口等信息。这些栈帧就存储在栈中。如果栈深度大于虚拟机允许的最大深度,则抛出StackOverflowError异常。
OutOfMemoryError StackOverflowError
java堆 java栈
内存空间不够了(需要及时释放内存) 栈深度超过范围了(比如:递归层数太多了)
* 本地方法栈:本地方法栈的主要作用就是支持native方法,比如在java中调用C/C++
* 方法区:方发区被各个线程共享,用于存储静态变量、运行时常量池等信息。
内存区域调节参数

*
Xms:初始堆大小,默认为物理内存的1/64(<1GB);默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制
* Xmx:最大堆大小,默认(MaxHeapFreeRatio参数可以调整)空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制
* Xmn:新生代的内存空间大小,注意:此处的大小是(eden+ 2 survivor space)。与jmap -heap中显示的New
gen是不同的。整个堆大小=新生代大小 + 老生代大小 + 永久代大小。
在保证堆大小不变的情况下,增大新生代后,将会减小老生代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
*
XX:SurvivorRatio:新生代中Eden区域与Survivor区域的容量比值,默认值为8。两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10。
*
Xss:每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。应根据应用的线程所需内存大小进行适当调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。一般小的应用,
如果栈不是很深,
应该是128k够用的,大的应用建议使用256k。这个选项对性能影响比较大,需要严格的测试。和threadstacksize选项解释很类似,官方文档似乎没有解释,在论坛中有这样一句话:”-Xss
is translated in a VM flag named ThreadStackSize”一般设置这个值就可以了。
* XX:PermSize:设置永久代(perm gen)初始值。默认值为物理内存的1/64。
* XX:MaxPermSize:设置持久代最大值。物理内存的1/4。
GC回收机制

* 哪些内存需要回收
* 什么时候回收
* 怎么回收
1、哪些内存需要回收
  java堆、方法区的内存

线程私有 线程共享
程序计数器、虚拟机栈、本地方法栈 java堆、方法区
随线程生而生,随线程去而去。线程分配多少内存都是有数的,当线程销毁时,内存就被释放了 堆和方法区的内存都是动态分配的(使用new关键字),所以也需要动态回收。
这部分内存的回收依赖GC完成
2、什么时候回收

* 引用计数法
* 可达性分析
(1)引用计数法
  给对象添加一个引用计数器,每当有一个地方引用它时,计数器加一。反之每当一个引用失效时,计数器减一。当计数器为0时,则表示对象不被引用。
举个例子:
Object a = new Object(); // a的引用计数为1 a = null; // a的引用计数为0,等待GC回收
(2)可达性分析
  设立若干根对象(GC Root),每个对象都是一个子节点,当一个对象找不到根时,就认为该对象不可达。
java中,可以作为GC Roots的对象包括:

* java虚拟机栈中引用的对象
* 方法区中静态变量引用的对象
* 方法区中常量引用的对象
* 本地方法栈中引用的对象
3、怎么回收

* 标记–清除算法
* 复制算法
* 分代算法
(1)标记——清除算法
  遍历所有的GC Root,分别标记处可达的对象和不可达的对象,然后将不可达的对象回收。
  缺点是:效率低、回收得到的空间不连续

(2)复制算法
  将内存分为两块,每次只使用一块。当这一块内存满了,就将还存活的对象复制到另一块上,并且严格按照内存地址排列,然后把已使用的那块内存统一回收。
  优点是:能够得到连续的内存空间
  缺点是:浪费了一半内存

(3)分代算法
  在java中,把内存中的对象按生命长短分为:

* 新生代:活不了多久就go die 了,比如局部变量
* 老年代:老不死的,活的久但也会go die,比如一些生命周期长的对象
* 永久代:千年王八万年龟,不死,比如加载的class信息
有一点需要注意:新生代和老年代存储在java虚拟机堆上 ;永久代存储在方法区上

回收方法
新生代 使用复制算法
老年代 使用标记——清除算法
永久代(JDK8被删)
java finalize()方法:在被GC回收前,可以做一些操作,比如释放资源。有点像析构函数,但是一个对象只能调用一次finalize()方法。

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:ixiaoyang8@qq.com
QQ群:637538335
关注微信