JVM 就是 java 虚拟机,我们可以把它理解成一个操作系统,每个不同的平台都有不同的 JVM,比如 Linux 系统和 Windows 系统,就是因为这个原因所以 Java 程序就有了一个很突出的特性就是 跨平台性
栈:在jvm中栈用来存储一些对象的引用、局部变量以及计算过程的中间数据,在方法退出后那么这些变量也会被销毁。它的存储比堆快得多,只比 CPU 里的寄存器慢
堆:用来存储程序中的一些对象,比如你用 new 关键字创建的对象,它就会被存储在堆内存中,但是这个对象在堆内存中的首地址会存储在栈中。
栈内存在 JVM 中默认是 1M,可以通过下面的参数进行设置:
-Xss
最小堆内存在 JVM 中默认物理内存的 64 分之 1,最大堆内存在 JVM 中默认物理内存 4 分之一,且建议最大堆内存不大于 4G,并且设置 -Xms=-Xmx 避免每次 GC 后,调整堆的大小,减少系统内存分配开销:
-Xms
-Xmx
在 jvm 的堆内存中有三个区域:
1.年轻代:用于存放新产生的对象。
2.老年代:用于存放被长期引用的对象。
3.持久带:用于存放 Class,method元信息。
JVM 内存垃圾收集算法
- 引用计数器的方法
类似于 Objective-C 的内存回收,在 OC 语言不使用 arc 机制时,在创建对象,且被引用时候也会对计数器加 1,使用完成后会调用 release 方法就会对计数器减 1
jvm 的这个算法也和上面的机制类似,但是他不能解决对象循环引用问题,也不好解决精确的计算,因为 java 程序开发,内存回收是对我们透明的,而 OC 是在代码层面自己去手动控制
- 根搜索算法
有向图算法,从 GC Roots 开始向下面搜索,搜索走过的路径叫做引用链接,当一个对象到达 GC Roots 没有任何引用链时,则这个对象就是不可达对象,就可以被回收,但是回收的时候会筛查出覆盖了 finalze() 方法且该对象 finalze() 方法没有被虚拟机调用过放入 F-Queue 队列然后执行 F-Queue 队列中对象的 finalze() 方法后如果该对象重新与某个 GC Roots 对象相关联,那么会将该对象从回收队列中移除
比如 jvm 中栈中指向堆中对象的指针就可以理解成一种 GC Roots,因为栈中保存的是对象的指针指向的是堆中对象的首地址,当栈中的指针没有了那么堆中的对象就是不可达对象,就会被GC回收
jvm内存垃圾回收算法
- 复制算法 (用于新生代)
用于新生代从 Eden 区到 survivor0 或者 survivor1 移动的时候
从根集合扫描如果对象被引用,就会被 copy 到 survivor0 或者 survivor1,然后剩下的都是不可达对象,就可以被回收掉,然后 S0 或者 S1 的对象就会到老年代中,在存活对象比较少的时候很高效并且不会产生内存碎片,就是内存需要额外划分一块 survivor 区出来
- 标记清除算法 (用于老年代)
就是使用根搜索算法扫描,如果可达就标记,当扫描完成后,就对未标记的对象进行回收,它不需要像复制算法一样,需要一个新的内存区,而且不会对对象进行 copy,这种对对象存活的多的情况下很高效,但是这样会产生内存碎片
- 标记整理压缩算法 (用于老年代)
就是在标记清除算法之后对内存碎片进行整理,只是他会对没有清理的对象进行移动,代价高