您的位置:首页 > 其它

软件构造第八章复习总结

2019-06-19 10:30 239 查看

OS通过虚拟内存机制 管理物理内存的使用,将物理内存映射为虚拟内存, 为每个进程分配虚拟内存空间。
对象模型
▪对象通常在堆中分配。 ▪对象引用是指向对象的指针(通常只是对象开头的堆地址)。 ▪变量包含对象引用(忽略原始数据)。 ▪每个对象都可以包含许多变量,从而引用其他对象。 ▪引用最多附加到一个对象,对象可以附加到两个或多个引用
静态分配 - 在程序启动时进行分配 - 基本上内存由编译器布局
▪动态分配 - 在程序执行时分配新对象。
Entity是指属性、参数、 局部变量和结果等在代码中的名字,其值为对象或对对象的引用。
Attach将entity同object关联/绑定在一起
Static mode
在程序执行期内 entity至多attach一个运行时对象。 该技术在程序加载时或开始时一劳永逸地为所有对象分 配空间(并将它们附加到相应的实体) 不支持递归 不支持动态创建数据结构
Stack-based mode
Stack是存储方法调用和局部变量的地方。 - 如果调用方法,则将其堆栈帧(栈帧)放在调用堆栈的顶部。 - 堆栈帧保存方法的状态,包括执行哪行代码和所有局部变量的值。 - 堆栈顶部的方法始终是该堆栈的当前运行方法
一个entity在运行时 可以先后attach多个对象,运行时机制以堆栈中的后进先出顺序分配和释放这 些对象。 当一个对象被释放时,相应的entity会再次attach到之前attached的对象 (如果有的话
Heap-based mode
将内存分为多份,每份保存 对象或未使用
也称为自由模式,可通过显式请求动态创建对象的 完全动态模式。
一个entity可先后attached任意数量的对象; 对象创建的模式 在编译时通常是不可预测的, 对象还可以包含对其他对象的引用。
支持创建复杂的动态数据结构
每个线程有自己的栈
栈中包含所有方法的 局部变量
线程只能访问自己的线程栈
线程创建的局部变量其他线程不可见
即使两个线程的代码一样,创建的同名变量仍然在各自的栈中
每个线程有自己 版本的局部变量
对象类型数据保存在堆中
如果对象被指派到某个局部变量,或者作为其 他对象的成员变量,创建的对象仍然在堆中。
局部变量引用了对象,引用保存在栈中,对象 本身存储在堆中
对象包含的方 法和方法包含的局部变量存储在栈中
对象的成员变量同对象一起存储在堆中, 不论成员变量的类型是基本类型还是对象类型(对其他对象的引用)
静态的类变量同类的定义一起保存在堆中
堆中的对象可以被所有拥有引用的线程访问
如果两个线程同时调用同一个对 象上的一个方法,它们都可以访 问该对象的成员变量,但是每个 线程都有自己的局部变量副本
Native Stacks本地方法栈 - 管理JVM使用的本机方法(用C编码)
▪程序计数器寄存器(PC)程序计数器 - 如果当前线程正在执行java方法,则PC记录执行方法的Java字节码地址。 - 如果当前线程正在执行本机方法,则PC为空。
▪方法区域方法区 - 方法区域也称为永久区域。 它主要包含常量和类的定义信息
静态模式中不存在内 存空间回收问题
基于块结构的语言 中:在给定块中声明的所有实体同时发生对象分配,从而允许为整个程序 使用单个堆栈。
根集合由系统的root对象以及局部entity、子程序的参数或返回值构成
Root的确定是语言相关的
这些起源/根的任何依赖,直接或间接是可达的,并且任何其他对象都无法访问:
在free模式下管理内存,第一步 是区分可达对象和不可达对象
识别垃圾并释放它占用的内存称为垃圾收集(GC)。
Pairing Principle
一种策略:让产生内存分配的对象也作为释放该内存的对象
Ownership Concept
只有拥有者被允许释放该对象,最后的拥有者执行释放,每个拥有者或者 传递所有权或者释放。
Reference counting
参考计数
▪引用计数:共享所有权的机制
▪目标:确定您何时是唯一所有者,从而您可以做出处置决定。 ▪基本思路:计算活动对象的引用数。 - 每个对象都有一个引用计数(RC) - 复制引用时,引用的RC递增 - 当引用被删除时,引用的RC递减 - 当RC = 0时可以回收一个对象

标记 - 扫描和标记 - 紧凑型
通过 跟踪活动对象的引用来查找其他活动对象。
每个对象都有一个标志,标志是否属于live集



方法1:递归遍历进行标 记,遍历对象图的最小生成树
方法2:标记栈,重复直到标记栈为空
Fragmentation and Copying
碎片:无法使用可用内存 - 外部:分配的内存分散到块中; 空闲块无法合并 - 内部:内存管理器分配的空间超出实际需要的空间 - 常见原因是标头,舍入大小增加▪碎片也是显式内存管理器的问题; free()通常不是免费的
GC是一个集合分区问题 - 标记位是定义两个集合的一种方法。
▪Mark-compact将实时集的成员物理移动到堆的不同部分

  • 自由指针标记实时数据和可以覆盖的内存之间的分界线
    ▪复制集合是一种更简单的解决方案:它可以选择实时对象和副本 他们到了一个“新鲜”的堆
    GC是一个集合分区问题 - 标记位是定义两个集合的一种方法。
    ▪Mark-compact将实时集的成员物理移动到堆的不同部分 - 自由指针标记实时数据和可以覆盖的内存之间的分界线▪复制集合是一种更简单的解决方案:它可以选择实时对象和副本 他们到了一个“新鲜”的堆.

‘. Java垃圾回收将堆划分成不同的区域(generation代), 以便GC可以更快地识别可以删除的对象

新对象分配到young generation中
GC 后仍然存活的对象,提升到old generation中
PermGen/Metaspace中 保存VM和class的元数据,以及类的静态变量
In the young generation
每次GC会发现大量死亡对象,少量存活对象
复制算法适合,代价低
In the old generation
对象存活率高,适合采用标记算法
PermGen中保存类的定义、静态方法 、静态对象的引用等内容
缺点: PermGen的容量是固定的(缺省或指定), 固定的容量容易导致运行时 的内存溢出错误
n. Java 8 开始,用Metaspace替代了PermGen,区别在内存分配方面
Metaspace的容量是自动增长的,此外,当 类的元数据使用达到了metaspace最大值时,会自动触发GC
三个空间任何一个已满,且存 在对空间的额外需求时,会发生GC
年轻代中内存不够时,发生Minor GC
Minor GC后存活的对象升级到老年代
老年代空间不够时,进行full GC
full GC针对年轻代、老年代和永久 代,当没有空间提供给minor GC将对象提升到老年代中,或者永久 代中无空间保存class元数据时发生。
年轻一代分为伊甸园空间和幸存者空间。 幸存者空间分为FromSpace(s0)和ToSpace(s1)。 ▪在Eden空间中分配新对象。 ▪在GC开头,所有对象都在Eden Space或FromSpace中,而ToSpace为空。
GC时,Eden区中所有存活的对象都 会被复制到ToSpace中,FromSpace区中仍存活的对象会根据他们的年龄值 来决定去向。年龄达到一定值(年龄阈值,可以通过 XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达到阈 值的对象会被复制到ToSpace中。
经过这次GC后,Eden Space和 FromSpace区被清空, FromSpace和ToSpace互换角色。ToSpace始 终为空。Minor GC会一直重复这样的过程,直到ToSpace区被填满 ,此时会将所有对象移动到年老代中。
吞吐 量:总时间中非用于垃圾回收的时间
需要根 据各种情况权衡代的容量,一个代的容量不影响其他代的回收频率和 暂停时间。
没有普适的容量设置准则,应根据应 用的内存使用和用户需求进行具体问题具体分析
. 最佳做法是将GC时间控制在执行时间的 5%之内

JVM堆大小决定了虚拟机收集垃圾的频率 和时间长短。 较大的堆,GC速度慢,GC频率低
.如果根据内存需求 设置堆大小,则完整垃圾回收速度会更快,但会更频繁地发生。
Java –Xms 1024M 年轻代和老年代之和的初始值 – Java –Xmx 2048M 年轻代和老年代之和的最大值 -XX: NewSize=[g|m|k] 最小值
-XX: MaxNewSize最大值
初始、最小、最大为同一值
老年代大小同年轻代大小相关
XX:MaxMetaspaceSize,最大空间,默认没有限制。
-XX:MetaspaceSize,初始空间大小,达到该值就会触发垃圾收集。同时GC 会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了 很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值
-XX:MinMetaspaceFreeRatio 在GC之后,最小的Metaspace剩余空 间容量的百分比。
XX:MaxMetaspaceFreeRatio
在GC之后,最大的Metaspace剩余空间容量的百分比
The serial garbage collector
串行收集器,使用一个线程进行垃圾回收,执 行时会冻结所有的应用线程,不适合多线程应用。
The throughput (parallel) collector 对年轻代的回收采用并行方式(多个线程) ,对 老年代的回收还是单线程
The Concurrent Mark Sweep (CMS) 利用多垃圾回收线程,适用于短回 收暂停,且能够在应用程序运行时与垃圾收集器共享处理器资源。
G1 (Garbage First) Garbage Collector 适用于运行在多 处理器大内存空间的应用程序。
详细的垃圾收集选项(verbosegc)使您可以准确测量垃圾收集的时间和资源。
数据缓冲区是物理内存空间,用来临时存储数据
通常用软件实现buffer机制,利 用速度快的RAM
缓冲区通常用于接收数据的速率 与处理数据速率之间存在较大差异的情况下,或者在速率可变的情况 下(例如打印机池或在线视频流)。
▪ 上层应用组件不需要等待下层组件真实地接受全部数据,即可返回操 作,加快了上层组件的处理速度,从而提升系统整体性能。
NIO: New I/O
通过一组buffer类,允 许将数据从JVM移动到OS,只需最少的内存间复制。 统一的通道类,允许数据直接从缓冲区输入到文件 和sockets. Buffer对象是 数据的容器
. 缓冲 区能够容纳的数据元素的最大数量,在缓冲区创建时被设定,不能改变。
缓冲区的第一个不能被读或写的元素。或者说,缓冲区中现存元素的计数。 . 下一个要被读或写的元素的索引,会自动由相应的 get( )和 put( )方法 更新。 Buffer中一个特定的position,之后可以通过调用reset()方法恢复到这 个position。
动态程序分 析:根据程序的一次或多次执行的过程与结果,分析代码在时空性能 方面所展现出的性质
为使动态分析有效,目标程序必须执行足够多次,以观察 到完整的、不同的执行行为通过分析代码覆盖度,确认动态分析是否已经足够。 要尽可能小的影响程序原本的执行,否则性能测量不准确 利用分析器检测源代码或执行程序以获取信息
实时或周期 性的获取/展示程序运行过程中的数据
提供了在执行期间在任何期望的点处开启或关闭 trace的机会
提供了在关键点暂停异步进程以更详细地检查与其他并行进程 的交互
代码注入/代码插入:在原始程序中加入某些语句来收集运行时数 据,这些语句不改变原程序的语义,但对原程序的性能有了轻微改变
采样:以特定的频率观察程序执行在特 定时刻所展现出的行为与状态
周期性监控被测程序,存储各时刻的快照

借助于虚拟机获取程序性能数据,所有JVM执 行的指令都被记录下来
jstat获取JVM的heap使用和GC的性能统计数据
jmap输出内存中的 对象分布情况
jhat 导出heap dump,浏览/查询其中的对象分布情况
jstack获取Java线程的stack trace
定位线程出现长时间停顿的原因,如多线程间死锁、死循环、请求外部资源 导致的长时间等待等。 – 线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没 有响应的线程到底在后台做什么事情,或者等待什么资源。

jps (JVM Process Status Tool) 虚拟机进程状况工具 列出当前运行的JVM 进程
jmap -dump:format=b,file=filename 导出heap dump
jmap -heap option is used to obtain Java heap information:
jmap -histo获取堆中类的特定柱状图
Shallow heap 某个对象消耗 的实际内存
Retained set:如果某对象被GC,所释 放的内存
支配树展示了内存导出文件中最大的对象
在代码层面 调优之前,应首先考虑以下策略:
不要过分强调不需要的性能 – System design: High-level architecture design
高层的架构设计 – Class and function design: data types and algorithm performance
更换数据 类型或算法 – Operating-system interactions: Hidden OS calls within libraries which are maybe slow or fat
是否由类库中隐藏的对OS的调用导致的问题 – Code compilation: “Automatic” optimization
优化编译器
– Hardware: update HW would be much easier 升级硬件
调优的目标就是找到20%的热点,并优化

Everyday use – Buffered{Reader, Writer} ▪ Casual use - Scanner – Easy but not general and swallows exceptions ▪ Stream integration – Files.lines – No parallelism support yet ▪ Async – java.nio.AsynchronousFileChannel 异步文件通道 ▪ Many niche APIs, e.g. memory mapping, line numbering – Search them out as needed 还有很多有用的API,根据需要使用 ▪ Consider Okio if third party API allowed

Singleton Pattern 单例模式
某些类在概念上只有一个 实例
只创建一个对象,然后复用
对管理重用的代码进行封装
确保类只有一个实例,并提供一个全局访问点
只能有一个类的实例,并且客户端 可以从众所周知的访问点访问它
当唯一的实例可以通过子类扩展时,客户端应该能够使用扩展实例而不修 改它们的代码
Flyweight Pattern 享元模式
享元模式描述了 如何共享对象,有效支持细粒度的使用而不会产生过高的成本
y. flyweight是一个共享对象,可在多个情境下同时使用
内在状态:不变的状态,可以共享
外在状态:状态是不固定的,使用时需要计算,不可共享
保存可共享的状态,对象可共享
.应用程序使用了大量的对象,由于 对象的数量庞大,存储成本很高 对象的大多数状态是外部状态,一旦删除了外部状态,可以用相对较少的 共享对象替代很多组对象
应用程序不依赖于对象标识
Prototype Pattern 原型模式
通过复制已有原型对象新 创建对象
目标:创建或初 始化对象代价高时,可通过此模式创建相似对象,降低开销
类似于文档模板,创建一次, 多次被复制使用,作为撰写文档的起点。
原型模式在初始化创建第一个对象时开销大,然后将 这些值作为原型存储在存储库中。
需要再次创建相同或类似对象时,只需从存储库中获取所有值已经预填 充的原型副本。
对 象clone意味着创建一个具有相同内容的副本 实现clone()时,要调用super.clone()(in Object)
类需要实现 java.lang.Cloneable接口
Shallow copy(浅拷贝
复制对象A的 所有字段到对象B中.
基本数据类型采用复制值的形式 对对象的引用,则复制引 用,此时A和B共享对同一对象的引用。
deep copy(深拷贝),
被 复制对象中引用了某对象时,需要创建新的被引用对象(不是共享)
A和B完全独立
实现复杂,当存在复杂的引用关系 图时更加难以实现
,原型模式用于创建本质上相似的新 对象(因此它是一种创建模式),
flyweight模式用于允许应用程序指向对象的同一个实例以节省内 存(因此它是一种结构模式)。
Object Pool Pattern

重复使用,避免重新创建
已经有了一个对象,并不再使用时,可让程序其他部分继续 使用
使用 时从pool中取出,用完后归还,类似工具箱
object pool的原理是复用,pool中object被取出后,只能由一个client使用; – Flyweight的原理是共享,一个object可被多个client同时使用。
object pool中的对象是同质的,对于client来说,可以用pool中的任何一个 object。如:需要connection时可以从connection pool中任意的拿一个出来 使用; – Flyweight的每一个实例的内部属性相同,但是外部属性不同,是异质的。 Flyweight使用时,是去FlyweightFactory中找一个特定的对象出来(如果 没有的话,就创建一个)。
bject pool对应的场景是对象被频繁创建、使用,然后销毁。每个对象的生 命期都很短。同一时刻内,系统中存在的对象数也比较小。
Flyweight对应的情况是在同一时刻内,系统中存在大量对象。
Most container objects (e.g., Vectors, Hashtables) can be reused rather than created and thrown away. 对容器对象进行重用
Canonicalizingobjects:
规范化的对象:用少量对象替代一个对象的大量副本
“” 比较比equals()快,通过规范化可实现用 “”比较替代equals()比较
Integer类中,缓存了从-128到127之间的所有的整数对象
枚举需要的内存少于等效字符串,并使网 络传输速度更快。
基本数据类型与其所在对象同时回收,影响较小
临时基本数据类型在栈中,不需要垃圾回收
持有int的对象,只需要回收1个对象(持有对象); 持有Integer对 象的对象,需要回收2个对象(持有对象和Integer对象)
尽量使用原始数据类型而不是其他对象数据类型
减少正在使用的临时对象的数量,特别是在循环中
利用new方法创建对象时,涉及的构造链会 自动执行。开销大。
应该检查构造函数层次结构以消除 对实例变量的任何多重初始化。
可以使用clone()替代构造方法,以 避免链式构造
在应用程序中有空 闲时间时尽早创建对象,并保留这些对象直到需要为止
字符常量池是堆中的内存区域,保存了 string对象
使用字符串文字:String s =“java”; - 在双引号内写的字符串称为字符串文字。 - 每当我们创建字符串文字时,JVM都会检查字符串常量池。 如果字符串已经存在于池中,则获取其引用。 - 如果池中不存在该字符串,则会在其中创建新的字符串对象。 ▪使用new关键字:String s = new String(“java”); - 当我们使用new关键字创建字符串时,它会转到堆。
‘String.substring().使用不会复制字符串字符的 String方法
String.toUppercase() and String.toLowercase().避免使用复制字符串字符的低效String方法
使用字符串连接运算符(+)在编译时创建字符串
运行时使用 StringBuffers
操作char数组中的 字符,而不是使用String和StringBuffer操作。

第八章主要讲解了 内存管理模型:堆、栈 ▪ GC,root、reachable、 unreachable、live、dead ▪ GC的四种基本算法 ▪ Java/JVM的内存管理模型:各 区域、各区域的GC方法 ▪ JVM GC性能调优:参数配置、 GC模式选择 ▪ Java性能调优工具:jstat, jmap, jhat, Visual VM, MAT ▪ Memory dump ▪ Stack trace
▪ Java代码调优的设计模式: singleton, prototype/cloneable, flyweight, pool ▪ 常见的Java I/O方法
重点理解堆,栈,垃圾回收的处理。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: