1.内存管理
系统和应用级别的内存管理
本机内存是分配给一个进程的物理内存,由OS管理。cpu执行指令,从内存读/写数据,在虚拟内存空间给进程分配内存,并与物理内存建立映射表
因为内存不足,所以要考虑高效率的内存管理问题,动态分配和回收内存,复用已回收的内存。
对象在heap堆中分配内存,对象引用指向其他对象在堆中的起始地址;非基本数据类型的变量等价于对象引用;每个对象可包含一组变量,每个变量可指向其他对象的引用;对象引用只指向一个其他对象,而一个对象可被多个其他对象引用。
![](https://i-blog.csdnimg.cn/blog_migrate/b6e75660358cb2b0bc2af5af82f322b8.png)
2.三种对象管理模式
静态内存分配在编译阶段就确定好了内存分配。
动态内存分配在运行时动态分配内存,建立新的内存对象,基于堆和栈的内存管理都是动态分配。
![](https://i-blog.csdnimg.cn/blog_migrate/60639e67dbc640167147e7517b5c846d.png)
差异在于如何和何时再程序对象与内存对象之间建立联系。
Static mode:在将程序load进内存的时候或开始执行的时候,确定所有对象的分配,不支持递归,不支持动态创建的可变长的复杂数据类型。
Stack-based mode:存储方法调用及方法执行中的局部数据,无法支持复杂类型。
Heap-based mode:在一块内存里分为多个小块,每块包含一个对象,或者未被使用。代码中的一个变量可以在不同时间被关联代不同的内存对象上,无法在编译阶段确定。内存对象也可以进一步指向其他对象。堆是自由模式的内存管理,动态分配,可管理复杂的动态数据类型。适用于:某些对象的延续时间比创建它的方法延续时间更长;递归、长度可变的数据结构;经常使用不限定长度的数据结构。
3.Java内存管理模型:关注JVM如何进行基于”堆“和”栈“的内存管理。
![](https://i-blog.csdnimg.cn/blog_migrate/8d67fee239ad9da6af2993b6ab525b7f.png)
每个线程在JVM中都有自己的栈,管理局部数据,各栈之间不可见。所有局部的基本数据类型都在栈上创建,多线程之间传递数据,是通过复制而非引用。
所有对象都在堆上创建,即使是局部变量的对象。
![](https://i-blog.csdnimg.cn/blog_migrate/f8e10560dddcb68df8d608f70d5c25a6.png)
堆上创建的对象可被所有线程共享引用;可访问对象,就可以访问对象内的成员;如果两个线程调用一个对象上的某个方法,它们分别保留该方法局部变量的拷贝。
4.垃圾回收
内存回收的模式
在静态内存分配模式下,无需进行内存回收,所有都是已确定的;
在栈上进行内存空间回收,按block(某个方法)整体进行;
在堆上进行内存空间回收最复杂——无法提前知道某个Object是否已变得无用。
可达和不可达
内存回收的首要问题是把可达对象和不可达对象分离开来。
通常,root包括:静态区域的数据;寄存器;目前执行栈中数据指向的内存对象。
从root可达的对象是活对象,不可达是死对象。
![](https://i-blog.csdnimg.cn/blog_migrate/758ab0e7a54ebe19265958877fda76c8.png)
垃圾回收器根据对象的活性,即从root的可达性来决定是否回收该对象的内存。死对象就是需要回收的垃圾。
垃圾回收GC:识别垃圾对象,把其中占用的内存加以回收。
健全性:每个收集到的对象都无法访问。
完整性:每一个不可达的对象都将被收集。
5.垃圾回收的基本算法
引用计数
对库中的每个物体上加上一个注释,指出对物体的实时参考数量。如果一个对象的引用计数为零,则抛出该对象(它已经死了)。
基本思想:为每个对象存储一个计数RC,当有其他引用指向它是,RC++;当其他引用与其断开时,RC--;若RC==0,则回收它。
![](https://i-blog.csdnimg.cn/blog_migrate/d25cc333888547a801e2c503ef6f0f93.png)
优点是简单、计算代价分散,幽灵时间段。
缺点是不全面、并发支持弱、占用额外内存空间。
标记-清除
在需要的物体上放置一个注释(根部)。然后以递归方式记录活体对象所需的任何内容。之后,检查所有对象并丢弃没有注释的对象。
基本思想:为每个对象设定状态位(live/dead)并记录,即mark阶段;将标记为dead的对象进行清理,即sweep阶段。
![](https://i-blog.csdnimg.cn/blog_migrate/e956691150b2f2bf9dd1d3df178a04b4.png)
优点是指针操作无运行时间开销、与增变器松散耦合、不移动对象。
缺点是在扫描期间,必须检查内存中的每个位置、碎片化,幽灵时间长。
标记-整理
把标记加在需要的物体上,将任何带有标记的东西移到库的后面。回收库前面的所有对象(全部死掉)。
![](https://i-blog.csdnimg.cn/blog_migrate/21107837f0a9e5c73f5d09131b94fc93.png)
复制
将需要的物体移动到新的库。然后递归地移动新库中对象所需的任何东西。之后,回收旧库(其中的任何物体已经死亡)。
与标记-整理顶点区别在于不是在同一个区域内进行整理,而是将live对象全部复制到另外一个区域。
![](https://i-blog.csdnimg.cn/blog_migrate/9886ec44cf28055bb16970a6f55732e2.png)
6.JVM中的垃圾回收
Java GC将堆分为不同的区域,各区域采用不同的GC的策略,以提高GC的效率。
HotSpot虚拟机(Sun JVM)有三个主要空间:young generation、oldgeneration、permanent generation。
![](https://i-blog.csdnimg.cn/blog_migrate/b7e9fe1c92c374b353b11ffb907432fe.png)
![](https://i-blog.csdnimg.cn/blog_migrate/0da3dc5bb9213ecffd40cabaf476eaa9.png)
针对年轻代,只有一小部分对象可较长时间存活,故采用复制算法减少GC代价。
针对老年代,这里的对象有很高的幸存度,使用标记-清除或标记-整理算法。
只有当某个区域不能再为对象分配内存时,才启动GC,针对young generation,使用minor GC进行垃圾回收。Minor GC所需时间较短,如果经历多次minor GC仍存活 ,将其复制到oldgeneration。
如果old generation满了,则启动full GC,此时无法进行下一次minorGC,minor GC和full GC 独立进行以减少代价。
当 perm generation满了之后,无法存储更多的元数据,也启动full GC。
7.JVM中垃圾回收的调整
尽可能减少GC时间,一般不超过程序执行时间的百分之5;一旦初始分配给程序的内存满了,就抛出内存溢出异常;再启动程序时,可为其配置内存分配的具体大小。
堆的大小决定JVM将会以何种频度进行GC,每次GC的时间多长。这两个指标具体多少为优,需要针对特定应用来分析,较大的堆会导致发生GC次数较少,但时间变长。Old generation的尺寸不需要设置,根据其他参数的取值可计算得到。
![](https://i-blog.csdnimg.cn/blog_migrate/1dd14091b5a736dc264666de1f59640c.png)
堆的大小决定JVM将会以何种频度进行GC,每次GC的时间多长。这两个指标具体多少为优,需要针对特定应用来分析,较大的堆会导致发生GC次数较少,但时间变长。Old generation的尺寸不需要设置,根据其他参数的取值可计算得到。