1.JVM Memory
Java运行时数据区
Java虚拟机在Java程序运行过程中会把它管理的内存,划分为若干个不同的数据区域。
这些区域各自的用途,以及创建和销毁的时间,有的区域随虚拟机的启动而存在,有些区域依赖用户线程的启动和结束而建立和销毁。(比如说栈)
根据《Java虚拟机规范》内存Java虚拟机所管理的内存将会包括如上图的几个运行时数据区。
-
程序计数器
程序计数器(Program Counter Register)是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。(每个线程都有自己的程序计数器,线程隔离)
-
Java虚拟机栈
它描述的是Java 方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame )用于存储局部变量表、操作栈、动态链接、方法出口等信息。线程私有(线程隔离) -
本地方法栈(线程私有)
本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java 方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native 方法服务。 -
Java堆
此内存区域的唯一目的就是存放对象,一个JVM实例只存在一个堆,堆内存的大小是可以调节的。堆内存是线程共享的。 -
方法区(线程共享)
方法区(Method Area)与Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量 、静态变量等数据。
2.GC
内存管理的方式通常有两种:
-
显式的内存管理(C/C++)
内存管理(内存的申请和释放)是程序开发者的职责
常见问题:
**内存泄漏:**内存空间已经申请,使用完毕后未主动释放
**野指针:**使用了一个指针,但是该指针指向的内存空间 已经被free -
隐式的内存管理(Java/C#)
内存的管理是由垃圾回收器自动管理的
优点:增加了程序的可靠性,减小了memory leak
缺点:无法控制GC的时间,耗费系统性能
GC——Garbage Collection
在JVM中,GC的功能是由垃圾回收器来完成。
研究GC,就必须要回答下面3个问题:
如何确定“垃圾”
如何回收垃圾
何时触发GC
确定哪些对象已经变成了垃圾,最简单的算法就是引用计数法:
- 给对象添加一个引用计数器
- 每当一个地方引用它时,计数器加1
- 每当引用失效时,计数器减少1
- 当计数器的数值为0时,也就是对象无法被引用时,表明对象不可在使用
但是这个算法存在一个致命的缺陷,无法解决循环引用的问题,
为此,引入了另外一种跟搜索算法。
根搜索算法:
- 这个算法的基本思想是将一系列称为“GC Roots”的对象作为起始点
- 从这些节点开始向下搜索
- 搜索所走的路径称为引用链
- 当一个对象到所有的GC root之间没有任何引用链相连时,就认为该对象变成了垃圾
GC Roots包含哪些对象呢?
1.虚拟机栈中引用的对象
2.方法区中的静态属性引用的对象
GC中如何回收垃圾呢?
- 标记清除算法(Mark Sweep)
-
标记复制算法(Copy)
-
标记整理算法(Mark Compact)
-
分代收集算法(Generational Collection 商用)
1.根据对象存活周期的不同将内存划分为几块
2.把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最合适的收集算法
3.在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集
4.而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-整理”算法进行回收
GC相关概念:
- Shallow size
就是对象本身占用的内存大小,也就是对象头加成员变量占用内存大小的总和 - Retained size
是该对象自己的shallow size 加上仅可以从该对象访问(直接或者间接访问)的对象的shallow size之和 - Retained size
是该对象被GC之后所能回收的内存的总和
shallow size VS retained size:
GC触发的时机:
- 申请heap space失败后会触发GC回收
- 系统进入idle后一段时间会进行回收
- 主动调用GC进行回收
3.内存相关问题
Out of Memory case
- Heap OOM 堆溢出
- Stack Overflow 栈溢出
内存泄漏(Memory Leak)
是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果
内存泄露可能导致内存溢出,但不是必然导致内存溢出