- 程序计数器
- 栈
- 堆
- 方法区

一块大的区域,需要根据功能,来划分不同的小区域。
JVM内存是从操作系统里申请来的,之后堆这部分区域进行了划分。
内存中最小的区域,保存了下一条要执行指令的地址~~
指令 => 字节码~
程序要想运行,JVM就得把字节码加载起来,放到内存中,程序就会一条一条把指令从内存中取出来,放到CPU上执行,此时也就需要随时记住,当前执行到哪一条了~
CPU是并发式的执行程序,CPU不是只给你一个进程提供服务。
也正因此,操作系统以线程为单位进行调度,每个线程都得记录自己的执行位置,每个线程就都会有一个程序计数器。
局部变量 和 方法调用信息
方法调用的时候,每次调用一个新的方法,就会涉及“入栈”操作,每次执行完一个方法,就会涉及到“出栈”操作。
每个线程有一个 栈
内存中空间最大的区域
new出来的对象+对象的成员变量 在其中。
一个进程一个堆
多个线程共用一个堆
局部变量在栈上;成员变量和new对象,在堆上。
类对象
方法区的作用:用来存储被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
的。
把.class文件,加载到内存中,构成类对象

加载
先找到对应的 .class文件,然后打开并读取.class文件,同时初步生成一个 类对象
Loading中的一个关键环节,会把读取并解析到的信息,初步的填写到 类对象中。
连接
初始化
真正的对类对象进行初始化,尤其是针对静态成员


大的原则:

注意:只要这个类被调用到,就会先对这个类进行类加载(实例化、调用方法、调用静态方法、被继承等)
它是 类加载 中的一个环节
这个环节处于 Loading 阶段的
双亲委派模型,就是描述这个找目录过程,也就是上述类加载器是如何配合的?
AppClassLoader 在加载一个未知的类名时,它并不是立即去搜寻 Classpath,它会首先将这个类名称交给 ExtensionClassLoader 来加载,如果 ExtensionClassLoader 可以加载,那么 AppClassLoader 就不用麻烦了。否则它就会搜索 Classpath 。
而 ExtensionClassLoader 在加载一个未知的类名时,它也并不是立即搜寻 ext 路径,它会首先将类名称交给 BootstrapClassLoader 来加载,如果 BootstrapClassLoader 可以加载,那么 ExtensionClassLoader 也就不用麻烦了。否则它就会搜索 ext 路径下的 jar 包。
JVM中的垃圾回收机制(GC)
申请内存的时机一般都是明确的(需要保存某个数据,就需要申请内存),
但是释放内存的时间,则是不清楚的。
代码里,创建一个变量(申请一个内存),这个变量啥时候不再使用了?也不是那么容易能确定的
如果内存释放的时机有问题(内存还想要用,结果就被丢了),此时就很难受了
上面是内存释放太早了
要是我迟一点释放行不行呢?就像图书馆,占座~~
所以内存的释放,早了也不行,晚了也不行,需要恰到好处。
垃圾回收的劣势:
垃圾回收要回收些啥?
垃圾回收具体咋回收?
第一阶段:找垃圾/判断垃圾
基于引用计数(不是Java)
简单可靠高效 ,但是有两个致命缺陷!
基于可达性分析(Java)
起始位置GCroot
优点:
自身缺点:
第二阶段:释放垃圾
明确了谁是垃圾之后,接下来就是回收(释放)垃圾了
回收垃圾(释放垃圾)的三种基本策略:
标记-清除
标记就是可达性分析的过程
此时如果直接放掉,虽然内存还是还给了系统,但是被释放的内存是离散的(不是连续的)
这个问题非常影响程序的执行
复制算法
为了解决内存碎片,引入复制算法
直接把不是垃圾的,拷贝,把原本的空间整体释放
此时内存碎片问题迎刃而解
复制算法的问题:
标记-整理
针对复制算法,再进行改进。
类似于顺序表删除中间元素
这个方案空间利用率是高了,但是仍然没有解决复制/搬运元素的开销。
上述方案虽然能解决问题,但是都有缺陷
实际JVM中的实现,会把多种方案结合起来使用~~
“分代回收!!”
争对对象进行分类(根据对象年龄),一个对象熬过一轮GC扫描,就称涨了一岁
针对不同的年龄的对象,采取不同的方案!!
区域划分:
分代回收中,还有一种情况,有一类对象可以直接进入老年代(大对象,占有内存多的对象)
大对象拷贝开销大,不适用于复制算法
上面 找垃圾 释放垃圾都是算法思想,不是具体落地实现
在JVM中,真正实现上述算法的模块,就是垃圾回收器
Serial收集器是最基本、历史最悠久的垃圾收集器了。JDK1.3之前回收新生代唯一的选择。
Serial收集器作为HotSpot中client模式下的默认新生代垃圾收集器。
Serial收集器采用复制算法、串行回收和"stop-the-World"机制的方式执行内存回收。
除了年轻代之外,Serial收集器还提供用于执行老年代垃圾收集的Serial Old收集器。Serial Old收集器同样也采用了串行回收和"Stop the World"机制,只不过内存回收算法使用的是标记-压缩算法。
ParNew 收集器除了采用并行回收的方式执行内存回收外,两款垃圾收集器之间几乎没有任何区别。ParNew收集器在年轻代中同样也是采用复制算法、"Stop-the-World"机制。
ParNew 是很多JVM运行在Server模式下新生代的默认垃圾收集器。
上述是比较老的来及回收器
把整个内存,分成了很小的区域,Region
给这些Region进行了不同的标记
有的Region放新生代对象,有的放在老年代(不追求依赖GC就扫描完,分多次来扫)对于业务代码影响更小
重点记忆:Java11开始,垃圾回收器G1.