JVM知识点-陆续补充

前言

个人对JVM是抱着打牢了基础再去深入学习的想法,也难免在平时学习过程中会碰到一些JVM的知识,稍微整理一下想起什么就记录什么。
关于JVM的内容,很长时间应该只限于维护此篇。
若有理解错误,感谢指出!

JVM内存结构

JVM分成本地方法栈、虚拟机栈、程序计数器(以上三个为线程私有,如局部变量是存在栈内存中的,不存在线程安全问题)、方法区、堆区(垃圾回收期主要管理的区域)

程序计数器

程序计数器是一个比较小的内存区域,表示java代码执行到第几行。
程序计数器是唯一没有OutOfMemoryError的区域。
程序计数器执行的是java方法,所以执行本地方法的时候获取到的会是Undefined

虚拟机栈

线程执行方法时都会创建一个栈帧,主要储存局部变量表以及一些方法的出口等。
方法调用时,栈帧入栈,方法结束时,栈帧出栈。

局部变量表中存储着方法的相关局部变量,包括各种基本数据类型,对象的引用,返回地址等。局部变量表是在编译时就已经确定好的,方法运行所需要分配的空间在栈帧中是完全确定的,在方法的生命周期内都不会改变。

虚拟机栈中定义了两种异常,如果线程调用的栈深度大于虚拟机允许的最大深度,则抛出StatckOverFlowError(栈溢出);不过多数Java虚拟机都允许动态扩展虚拟机栈的大小(有少部分是固定长度的),所以线程可以一直申请栈,直到内存不足,此时,会抛出OutOfMemoryError(内存溢出)。

局部变量表内容越多,栈帧越大,栈深度越大。
-Xss可以设置栈的大小
就像水杯一样装的水越多就越深。

本地方法栈

与虚拟机栈相似,唯一的区别在本地方法栈储存的是本地方法。

方法区

方法区在HotSpot被作为一个永久代,除了HotSpot之外的多数虚拟机,并不将方法区当做永久代。
常量池是方法区的一部分,用于存储编译期就生成的字面常量、符号引用、翻译出来的直接引用(符号引用就是编码是用字符串表示某个变量、接口的位置,直接引用就是根据符号引用翻译出来的地址,将在类链接阶段完成翻译);

–以下没有过多去记
方法区是各个线程共享的区域,用于存储已经被虚拟机加载的类信息(即加载类时需要加载的信息,包括版本、field、方法、接口等信息)、final常量、静态变量、编译器即时编译的代码等。

方法区在物理上也不需要是连续的,可以选择固定大小或可扩展大小,并且方法区比堆还多了一个限制:可以选择是否执行垃圾收集。一般的,方法区上执行的垃圾收集是很少的,这也是方法区被称为永久代的原因之一(HotSpot),但这也不代表着在方法区上完全没有垃圾收集,其上的垃圾收集主要是针对常量池的内存回收和对已加载类的卸载。

在方法区上定义了OutOfMemoryError:PermGen space异常,在内存不足时抛出。

堆内存

堆内存是垃圾回收器最最重要的地方,在JVM中也是占的最大的一块。
堆区所有线程共享,在虚拟机启动时创建,主要储存对象的实例,一般情况下所有对象都会在堆内存上分配内存。

直接内存

除了JVM内存外的机器内存叫直接内存,可以调用一些C语言的方法将东西储存到直接内存,所以也会产生OOM。

GC机制过程

现在的垃圾回收期大多采用分代算法
堆内存被分为年轻代与年老代
年轻代被分为一个Eden区与两个Survivor(From Survivor 与 To Survivor)区
垃圾回收器主要是对Eden区与From Suvivor区进行操作

垃圾回收主要在空闲的时间对不可达的对象进行一个内存的回收整理。

基础的GC算法

标记-清理 效率慢,内存整洁度不高
停止-复制 效率高,整洁度高,但是浪费一般的内存,新生代采用
标记-清理 效率中,整洁度高,老年代采用

具体的GC收集机制暂未进行更深了解,如串行、并行等深入机制。

可达性分析

引用计数法
对象新增一个引用,那么计数增1.
根搜索法
从GC roots 开始向下搜索,经历的链路成为引用链,没有被引用链关联的引用,为不可用对象。

GC roots:
虚拟机栈中引用的对象。
方法区中静态属性引用的对象。
方法区中常量引用的对象。
本地方法栈中JNI引用的对象。

对于用可达性分析法搜索不到的对象,GC并不一定会回收该对象。要完全回收一个对象,至少需要经过两次标记的过程。

GC触发条件

当Eden区满时,触发Minor GC

调用System.gc时,系统建议执行Full GC,但是不必然执行
老年代空间不足
方法区空间不足
通过Minor GC后进入老年代的平均大小大于老年代的可用内存
由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小

整体来说出发Full GC更多是因为老年代放不下要存进来的对象

进入年老代的情况

1.Minor GC后,To Survivor存不下
2.超过JVM设置的某个内存,大对象直接进入年老代
3.超过JVM设置的年龄阙值,进入年老代
4.如果在From中,相同年龄所有对象的大小总和大于From和To空间总和的一半,那么年龄大于等于该年龄的对象就会被移动到老年代,而不用等到15岁(默认) —-此条件不是很理解

为什么经常设置-Xmx与-Xms相等

VM初始分配的内存由-Xms指定,默认是物理内存的1/64;JVM最大分配的内存由-Xmx指 定,默认是物理内存的1/4。默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制。因此服务器一般设置-Xms、-Xmx相等以避免在每次GC 后调整堆的大小。对象的堆内存由称为垃圾回收器的自动内存管理系统回收。

简单说就是JVM会根据空闲的堆内存比例来自动调整堆的大小,设置相等避免每次回收后都要跳转堆大小。

猜你喜欢

转载自blog.csdn.net/chijiandi/article/details/79923813