第八章--第一节:性能构造的度量、原理和方法

第八章:软件构造的性能

第一节:性能构造的度量、原理和方法


问题一:性能度量指标

    1.时间性能:每条指令、每个控制结构、整个程序的执行时间

    2.空间性能:每个变量、每个复杂结构、整个程序的内存消耗

  • 获取运行时间,第八章--第一节:性能构造的度量、原理和方法
  • 获取执行时间,第八章--第一节:性能构造的度量、原理和方法第八章--第一节:性能构造的度量、原理和方法

问题二:内存分配与内存管理

问题描述:因为内存不足,所以需要考虑高效率的内存管理问题

1.内存对象模型

  • 每个对象存储在内存中一段连续的空间中
  • 如果是引用,则存储它所指向的对象的内存地址

**重点:

  1. 对象在heap堆中分配内存
  2. 对象引用:指向其他对象在堆中的起始地址
  3. 非基本数据类型的变量等价于对象引用
  4. 每个对象可包含一组变量,每个变量可指向其他对象的引用
  5. 对象引用只指向一个其他对象,而一个对象可被多个其他对象所引用。

例:对象与对象引用

第八章--第一节:性能构造的度量、原理和方法

2.三种对象管理模式

    ①静态内存分配:在编译阶段就已经确定好了内存分配

    ②动态内存分配:在运行时动态分配内存,建立新的内存对象

(基于堆和栈的内存管理都是动态分配)

    一、静态模式(Static mode)

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

    二、基于栈的动态模式(Stack-based mode)

    含义:栈 ,存储方法调用以及方法执行中的局部数据(后进先出,无法支持复杂数据类型

    基于堆的动态模式(Heap-based mode)

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

    使用堆分配的原因:

  •     某些对象延续的时间比创建它的方法所延续的时间更长(所以stack不行)
  •     递归的数据结构,长度可变的数据结构(所以静态方法和stack都不行)
  •     经常要使用不限定长度的数据结构

问题三:java内存模型

    含义::JVM如何管理内存

  • 每个线程有自己的栈,管理其局部数据,各栈之间彼此不可见
  • 所有局部的基本数据类型都在栈上创建
  • 多线程之间传递数据,是通过复制而非引用
  • 所有对象都在堆上创建
  • 即使是局部变量的object,也是在堆上创创建
  • 堆上创建的对象可被所有线程共享引用
  • 可访问对象,就可以访问对象内的成员变量
  • 如果两个线程调用同一个对象上的某个方法,它们分别保留该方法的局部变量的拷贝。

**例:填空

第八章--第一节:性能构造的度量、原理和方法

***解析:

①基本数据类型的局部变量存储在栈中

②对象的引用的局部变量存储在栈中,对象本身存储在堆中

③对象的方法中的局部变量存储在栈中,方法所属的对象存储在堆中

④对象的基本基本数据类型的成员变量存储在堆中,对象的引用类型的变量存储在堆中

⑤静态变量存储在堆中

(对象在heap中分配内存,因此对象的成员变量也在heap中分配内存;对象的引用若为某对象的成员变脸自然在heap中储存,否则在若是作为局部变量,则在栈中储存)


问题四:垃圾回收

   1.内存回收的三种方式

    ①静态模式下的内存回收:在静态内存分配模式下,无需进行内存回收:所有都是已确定的。

    ②在栈模式下的内存回收:按block(某个方法)整体进行

    ③在堆模式下的内存回收:在heap上进行内存空间回收,最复杂——无法提前预知某个object是否已经变得无用。

    2.动态垃圾回收(Definition of Garbage Collection)

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

问题五:垃圾回收的基本算法

    1.引用计数(Reference counting)

    基本思想:为每个object存储一个计数RC,当有其他reference指向它时,RC++,当有其他reference与其断开时,RC--;如果RC==0,则回收它。

    优点:简单、计算代价分散,“幽灵时间”短0

    缺点:不全面(容易漏掉循环引用的数据)、要持续不断的计算(代价高)、难以支持并发、等。

    2.标记-清除(Mark-Sweep)

    基本思想:为每个object设定状态位(live/dead)并记录,即mark阶段;在将标记位dead的对象进行清理,即sweep阶段。

    示意图:

    第八章--第一节:性能构造的度量、原理和方法

    3.标记-整理(Mark-Compact)

    基本思想:对用标记-清除完后的内存空间进行整理。

    示意图:

    第八章--第一节:性能构造的度量、原理和方法

    4.复制(Fragmentation and Copying)

    基本思想:不是在同一个区域进行整理,而是将live对象全部复制到另一个区域。

    示意图:

    第八章--第一节:性能构造的度量、原理和方法

问题六:java虚拟机中的垃圾回收

    java虚拟机的做法:java GC将堆分为不同的区域,各区域采用不同的GC策略,以提高GC的效率。

(使用“-verbose:gc”在控制台或日志文件中输出JVM进行GC的全过程)

    堆的不同区域:

    第八章--第一节:性能构造的度量、原理和方法

① 针对年轻代(Young Generation):只有一小部分对象可较长时间存活,故采用copy算法减少GC代价。

②针对年老代:这里的对象有很高的幸存度,使用Mark-Sweep或Mark-Compact算法

  • 只有当某个区域不能再为对象分配内存时(满),才启动GC
  • 针对young generation,使用minor GC进行垃圾收集。如果历经多次minor GC仍存活下来,将其copy到old generation。
  • 如果old generation满了,则启动full GC(Old generation满,意味着无法进行下一次minor GC了)。
  • 当perm generation满了之后,无法存储更多的元数据,也启动full GC。
  • 第八章--第一节:性能构造的度量、原理和方法

问题七:在JVM中的垃圾回收的调谐

  • 尽可能减少GC时间,一般不超过程序执行时间的5%
  • 一旦初始分配给程序的内存满了,就抛出内存溢出异常,第八章--第一节:性能构造的度量、原理和方法
  • 在启动程序时,可为其配置内存分配的具体大小第八章--第一节:性能构造的度量、原理和方法
  • 堆的大小决定着VM将会以何种频度进行GC、每次GC的时间多长。这两个指标具体取值多少为“优”,需要针对特定应用进行分析。
  • 较大的heap会导致较少发生GC,但每次GC时间很长
  • 如果根据程序需要来设置heap大小,则需要频繁GC,但每次GC的时间较短
  • 第八章--第一节:性能构造的度量、原理和方法