JVM
类装载器
根加载器,像
Object
这些类,在一启动的时候,就通过
Bootstrap
启动类加载器加入
到了
jvm
,所以可以直接使用
扩展类加载器,在
jdk
不停的更新途中,加入了很多新的
jar
包,这些
jar
包就使用扩
展类加载器
Extension
加入到了
jvm
中
系统类加载器,自己在
java
项目中创建的
class
类,都会通过
AppClassLoader
系统类
加载器,加载当前应用的
classpath
的所有类进入到
jvm
中
双亲委派
一个类的加载先从根加载器找,根加载器找不到就去扩展类加载器找,扩展类找
不到就去系统类加载器中找。
沙箱安全机制
双亲委派机制就是为了保证沙箱安全机制,就不让人干坏事,不能替换掉
java
本
身自带的一些类
线程私有:
native
本地方法
native
是一个关键字
有声明,
java
无实现,是使用的
C
语言实现的第三方函数库,
线程私有:
PC
寄存器:程序计数器
行号计数器,用来存储执行到了第几行,多线程环境下会出现时间争抢,如果当前
线程时间片用完,那么就需要等待获取到新的时间片来进行执行,那么从等待重新
开始执行的时候,从哪一行开始执行就是用程序计数器来决定的
记录了方法之间的调用和执行情况,类似排版值日表,用来存储指向下一条指令的
地址,也就是将要执行的指令代码,它是当前线程所执行的字节码的行号指示器
线程私有:
stack
:栈
栈管运行,堆管存储
栈主管
java
程序的运行,在创建线程时创建,在线程结束时栈也释放,栈不存在垃
圾回收问题,只要线程一结束该栈就
Over
,声明周期和线程一致,是线程私有的
8
种基本类型的变量
+
对象的引用变量
+
实例方法都是在函数的栈内存中分配
栈帧钟主要存储三类数据
1
:本地变量,输入参数和输出参数以及方法内的变量
2
:栈操作,记录出栈,入栈的操作
3
:栈帧数据,包括类文件,方法等等
java
方法
=
栈帧
每个方法执行的同时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链
接、方法出口等信息,每一个方法从调用直至执行完毕的过程,就对应着一个栈帧
在虚拟机钟入栈到出栈的过程,栈帧的大小和具体
jvm
的实现有关,通常在
256K~756K
之间,约等于
1Mb
左右
java.lang.StackOverFlowError
:
SOF
:把栈撑爆了,是一个
Error
错误
线程共享:方法区
存在垃圾回收
方法区不是专门放方法的地方
供各线程共享的运行时内存区域,它存储了每一个类的结构信息,运行时常量池、
字段和方法数据、构造函数和普通方法的字节码内容
方法区是规范,在不同虚拟机里面实现是不一样的,最典型的就是永久代和元空间
空调
k1 = new
格力()
list list = new Arraylist
()
java 7
以前是:方法区
f = new
永久代
java 8
以后是:方法区
f = new
元空间
所以方法区是一个规范,
new
的后面是具体的实现
线程共享:
heap
:堆
新生代
伊甸区:
Eden
元区
new
出来的第一次都存放在这个区域
Eden
区满了会开启
ygc
轻
GC
,执行
ygc
后,大部门对象都会被
kill
掉,没有被
kill
掉的幸存者就会转移到幸存者
0
区
幸存者
0
区:
Survivor 0
接收没有被
ygc kill
掉的对象,幸存者
0
区和
1
区会进行交换,保证
0
和
1
两个区
域一个存放活着的对象,一个啥都不存
幸存者
1
区:
Survivor 1
被
GC
了
15
次(默认值
15
)没被
kill
掉的会转移到老年代
交换
from
区和
to
区,他们的位置和名分,不是固定的,每次
GC
后会交换,谁空谁
是
to
老年代
如果老年代区域也满了,就会开启
full gc = FGC
重
GC
,如果
full gc
多次,发现
空间没办法腾出来,那么
jvm
就报
OOM
堆内存溢出异常
理论
新生区是类的诞生、成长、消亡的区域,一个类在这里产生,应用,最后被垃圾
回收器收集,结束生命。新生区又分为两部分:伊甸区和幸存者区,所有的类都
是在伊甸区被
new
出来的。幸存区有两个:
0
区和
1
区。当伊甸园的空间用完时,
程序又需要创建对象,
JVM
的垃圾回收器将对伊甸园区进行垃圾回收
Minor GC
轻
GC
,将伊甸园区中的不再被其它对象所引用的对象进行销毁。然后伊甸园中
的剩余对象移动到幸存者
0
区,如果幸存者
0
区也满了,再对该区进行垃圾回
收,然后移动到
1
区,那如果
1
区也满了呢?再移动到养老区,若养老区也满
了,那么这个时候将产生
MajorGC FULLGC
,进行养老区的内存清理,若养老区
执行了
Full gc
之后依然无法进行对象的保存,就会产生
OOM
异
常
"OutOfMemoryError"
jdk8
以后元空间
/jdk7
之前永久代
堆内存调优
-Xms
:设置初始分配大小,默认为物理内存的
1 / 64
-Xmx
:最大分配内存,默认为物理内存的
1 / 4
-XX:+PrintGCDetails
:输出详情的
GC
处理日志
实践经验
生产环境上的
java
应用,
JVM
的
-Xmx
和
-Xms
两个参数的值必须一致,防止内
存忽高忽低产生停顿
java.lang.OutofMemoryError
:
java heap space
:堆内存溢出
GC
(分代收集算法)
次数上频繁收集
Young
区
次数上较少收集
Old
区
基本不动元空间
GC 4
大算法
引用计数法
jvm
实现不采用这种方式
有一个计数器去记录每个对象被引用的次数
缺点:较难处理循环引用,有个计数器消耗性能
复制算法
年轻代使用的垃圾回收算法就是复制算法
复制算法的基本思想就是将内存分为两快(
Eden
区和
from
区是一快
to
也是
一快),每次只用其中一快,当这一快内存用完,就将还活着的对象复制到
另外一快上面,复制算法不会产生内存碎片
因为
Eden
区对象一般存活率较低,一般的,使用两块
10%
的内存作为空闲和
活动区间,而另外
80%
的内存,则是用来给新建对象分配内存的,一旦发生
GC
,将
10%
的
from
活动区间与另外
80%
中存货的
Eden
对象转移到
10%
的
to
空
闲空间,接下来,将之前
90%
的内存全部释放,以此类推
优点:速度快
缺点:浪费空间 浪费了一半内存,这太要命了
标记清除
老年代使用的就是标记清除与标记整理的混合实现
算法分成标记和清除两个阶段
先标记出要回收的对象
然后统一回收这些对象
缺点:
内存碎片过多
速度没复制算法快,有标记和清除两个操作
优点:
不需要额外的空间
标记压缩
老年代使用的就是标记清除与标记整理的混合实现
也叫标记清除
+
标记整理算法,比标记清除多了一步整理的步骤
缺点:
速度慢
优点:
不会浪费空间和产生内存碎片
JDK9
之后
JAVA 9
的默认垃圾回收器
G1
垃圾回收器
G1
将新生代,老年代的物理空间划分取消了,这样我们再也不用单独的空
间对每个代进行设置了,不用担心每个代内存是否足够。
G1
算法将堆划分为若干个区域(
Region
),它仍然属于分代收集器
https://www.kancloud.cn/luoyoub/jvm-note/2255980
J