【软件构造】8.2 内存性能与垃圾回收

    xiaoxiao2024-10-17  4

    1. 操作系统与应用层面的内存管理

    内存管理:内存分配+垃圾回收 动态分配和回收内存;复用已回收的内存 每个对象存储在内存中一段连续的空间中,包括header和fields。 对象可以包含对其他对象的引用,则存储它所指向的对象的内存地址 为新对象分配内存的基本操作:

    在内存里创建一个新对象将其与某个引用关联起来初始化其内部各域

    对象模型:

    对象在heap堆中分配内存对象引用:指向其他对象在堆中的起始地址非基本数据类型的变量等价于对象引用

    2. 内存管理的三种模式

    静态内存分配:在编译阶段就已经确定好了内存分配 动态内存分配:在运行时动态分配内存,建立新的内存对象 基于堆和栈的内存管理都是动态分配

    2.1 静态

    Fortran 在将程序load进内存的时候或开始执行时,确定所有对象的分配,运行时无法改变 。 不支持递归,不支持动态创建的可变长的复杂数据类型

    2.2 动态,基于栈

    Pascal C 存储方法调用以及方法执行中的局部数据

    当方法被调用,它的栈帧被放到call stack的最上面栈帧保存着方法的状态,包括哪一行代码在执行和所有局部变量的值栈顶的方法是正在运行的方法 无法支持复杂数据类型

    2.3 动态,基于堆

    堆:内存的一部分,分为多个小块,每块包含一个对象,或者未被占用 代码中的一个变量ref可在不同时间被指向到不同的内存对象上,无法在编译阶段确定。内存对象也可以进一步指向其他对象。 自由模式的内存管理,动态分配,可管理复杂的动态数据结构

    3. Java内存模型

    3.1 栈

    JVM中运行的每个线程都有自己的线程栈(thread stack) 线程栈中的信息包括该线程在运行到当前点之前都调用过哪些方法,和运行的每个方法的所有局部变量。 一个线程只能访问自己的线程栈;被某线程创造的局部变量只对创造它的线程可见。 所有局部的基本数据类型都在栈上创建。 一个线程可以将原始变量的副本传递给另一个线程,但它不能共享原始局部变量本身。

    3.2 堆

    堆包含了Java应用中创建的所有对象,无论是哪个线程创建的。 包括基本数据类型的包装类(Byte,Integer,Long…) 作为局部变量的对象和作为其他对象成员变量的对象都在堆上存储。 堆上创建的对象可被所有线程共享引用。 如果线程可以访问对象,就可以访问对象内的成员变量。 如果两个线程调用同一个对象上的某个方法,它们分别保留该方法的局部变量的拷贝。

    3.3 JVM的内存结构

    https://stackoverflow.com/questions/79923/what-and-where-are-the-stack-and-heap 1.栈:向方法中传参数;返回方法的结果;存储表达式计算出来的立即数;存储局部变量 2.堆:存对象,数组 3.本地方法栈:JVM用来管理本地方法(C语言编写) 4.PC:代码行号指示器,用于指示,跳转下一条需要执行的命令。(指向的是Java指令) 5.Method Area:用于存储被VM加载的类信息、常量、静态变量等 HotSpot JVM中用Permanent Area (Perm)实现该区域,并作为heap的一部分 Java 8之后改名为Metaspace (使用native memory?本机内存(虚拟机外部))

    4. 垃圾回收

    4.1 三种模式下的内存回收

    在静态内存分配模式下,无需进行内存回收:所有都是已确定的 在栈上进行内存空间回收:按block(某个方法)整体进行 在堆上进行内存空间回收:复杂,无法提前预知某个object是否已经变得无用。

    4.2 可达与不可达对象

    活对象:从root可达的对象 死对象:从root不可达 从root对象开始进行有向图的搜索,将图分为root可达部分和root不可达部分,后者将被进行内存回收。

    4.3 垃圾回收的定义

    垃圾回收器根据对象的“活性”(从root的可达性)来决定是否回收该对象的内存。 “死”的对象就是需要回收的“垃圾”。 垃圾回收GC:识别“垃圾”对象,把其占用的内存加以回收。 GC代价的衡量标准:

    执行时间延迟时间(幽灵时间)所占用的内存/对程序所使用内存的影响 …

    5. 垃圾回收的基本算法

    5.1 Reference counting 引用计数

    基本思想:为每个object存储一个计数RC,当有其他reference指向它时,RC++;当其他reference与其断开时,RC–;如果RC==0,则回收它 (及其所有指向的object)。 优点:简单;计算代价分散;“幽灵时间”短 对count的修改是在每行代码执行后进行,而非集中进行 缺点:不全面(容易漏掉循环引用的对象);并发支持较弱;占用额外内存空间等

    5.2 Mark-Sweep 标记-清除

    标记:为每个object设定状态位(live/dead)并记录 清除:将标记为dead的对象进行清理 缺点:需要停止程序执行来Mark和Sweep,导致幽灵时间过长,也影响程序本身的性能;内存碎片化;时间复杂度高(O(heap))

    5.3 Mark-Compact 标记-整理

    标记不被清除的对象;把有标记的对象移到堆的最后区域,把前面的对象都回收。 优点:避免碎片化;剩余内存的相对位置保持不变; 缺点:时间消耗太长,影响程序本身

    5.4 Fragmentation and Copying 复制

    将live对象全部复制到另一个区域。 将堆分为两个区域:Fromspace和Tospace 在Tospace中为对象分配内存,当Tospace满时,交换两个区域的职能,挑出Fromspace中所有的live对象,复制到Tospace,更新引用,使用Tospace中的对象作为工作队列。

    6. JVM中的垃圾收集

    JVM自动回收不再使用的对象。 使用参数“-verbose:gc”在控制台或日志文件中输出JVM进行GC的全过程。 Java GC将堆分为不同的区域,各区域采用不同的GC策略,以提高GC的效率。 JVM有三个主要的区域: young generation, old generation, permanent generation Java应用程序中对象分配内存首先分配到young generation space,如果该对象经历了几次GC还存活,则移到old generation space。 GC策略: young generation:Copy old generation:Mark-Sweep或Mark-Compact 只有当某个区域不能再为对象分配内存时(满),才启动GC 针对young generation,使用minor GC进行垃圾收集。Minor GC所需时间较短,如果历经多次minor GC仍存活下来,将其copy到old generation。如果old generation满了,则启动full GC,Minor GC和full GC独立进行,减小代价 。 eden区域满的时候,触发minor GC. 被引用的对象移动到S0,清除eden时未被引用的对象被清除。 下一次minor GC,eden和S0中存活的对象移动到S1,eden和S0都被清空。 下一次minor GC,重复相同过程。 当一个对象在S0和S1中反复移动达到一定次数,它将被移动到old generation

    7. JVM中的垃圾收集的调优

    7.1 调整VM Heap Size

    堆的大小决定着VM将会以何种频度进行GC、每次GC的时间多长。这两个指标具体取值多少为“优”,需要针对特定应用进行分析。较大的heap会导致较少发生GC,但每次GC时间很长,如果根据程序需要来设置heap大小,则需要频繁GC,但每次GC的时间较短。 -Xms比-Xmx小时,young generation和old generation的总大小可可以随时间变化(在这两个参数之间变化)。heap尺寸变化时需要full GC。

    -XX: NewSize=<n>[g|m|k]:young generation区域的初始大小和最小大小。<n>是大小,[g|m|k]是单位。-XX: MaxNewSize=<n>[g|m|k]:young generation区域的最大大小-Xmn<n>[g|m|k]:young generation区域的初始、最小及最大大小-XX:NewRatio=<n>:young generation与old generation区域大小的比例是1:n-XX:SurvivorRatio=<n>:eden和survivor区域大小的比例是n:1-XX:MinHeapFreeRatio=<minimum> (default = 40%)-XX:MaxHeapFreeRatio=<maximum> (default = 70%) …

    7.2 调节GC方式

    1.-XX:+UseSerialGC:用单线程处理垃圾回收工作。 2.-XX:+UseParallelGC -XX:ParallelGCThreads=n:minor gc多线程,full gc单线程 3.-XX:+UseParallelOldGC:minor gc,full gc都是多线程 4.-XX:+UseConcMarkSweepGC -XX:ParallelCMSThreads=n 5.–XX:+UseG1GC

    7.3 输出/记录GC信息

    -verbose:gc 可将GC信息记录到log中,以便于后续分析

    7.4 手动请求GC

    System.gc()

    最新回复(0)