软件构造知识点复习整理(8-10章)

▪ 内存管理模型:堆、栈

三种对象管理模式

▪对象管理的形式(特别是在OO中)只是三种常见模式中的一种:

    静态的

    基于栈

    基于堆

    Entity是指属性、参数、局部变量和结果等在代码中的名字,其值为对象或对对象的引用。

    Attach将entity同object关联/绑定在一起。

    静态模式

    在静态模式下,在程序执行期内entity至多attach一个运行时对象。

    该技术在程序加载时或开始时一劳永逸地为所有对象分配空间(并将它们附加到相应的实体)

    不支持递归,不支持动态创建数据结构

基于栈的模式

堆栈是存储方法调用和局部变量的位置。

    如果调用方法,则将其堆栈帧(栈帧)放在调用堆栈的顶部。

    堆栈帧保存方法的状态,包括执行哪行代码和所有局部变量的值。

    堆栈顶部的方法始终是该堆栈的当前运行方法。

    一个entity在运行时可以先后attach多个对象,运行时机制以堆栈中的后进先出顺序分配和释放这些对象。

    当一个对象被释放时,相应的entity会再次attach到之前attached的对象(如果有的话)。

基于堆的模式

将内存分为多份,每份保存对象或未使用。

也称为自由模式,可通过显式请求动态创建对象的完全动态模式。

一个entity可先后attached任意数量的对象; 对象创建的模式在编译时通常是不可预测的, 对象还可以包含对其他对象的引用。

支持创建复杂的动态数据结构。

某些程序利用静态和stack分配结合的方式

有些利用动态分配

    某些对象的寿命比创建它们的方法长。

    递归数据结构,如列表和树。

    避免对数据结构大小进行固定的硬限制。

关于Java内存模型的一些要点

基本数据类型的局部变量保存在线程栈中

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

对象包含的方法和方法包含的局部变量存储在栈中

对象的成员变量同对象一起存储在堆中, 不论成员变量的类型是基本类型还是对象类型(对其他对象的引用)

静态的类变量同类的定义一起保存在堆中

堆中的对象可以被所有拥有引用的线程访问

当线程有权访问对象时,它还可以访问该对象的成员变量。

如果两个线程同时调用同一个对象上的一个方法,它们都可以访问该对象的成员变量,但是每个 线程都有自己的局部变量副本。

▪ GC,root、reachable、unreachable、live、dead

垃圾收集(GC)

静态模式中不存在内 存空间回收问题

动态模式(stack-based, heap-based)则需要考虑内存空间回收问题

基于堆栈的模式中的空间回收

基于块结构的语言 中:在给定块中声明的所有实体同时发生对象分配,从而允许为整个程序 使用单个堆栈。

基于堆(免费)模式的空间回收

Heap 模式中,对象的创建在编译时未知,所以无法预测对象何时无用(可回收)

root

根集合由系统的root对象以及局部entity、子程序的参数或返回值构成

Root的确定是语言相关的

    通常,确定根是依赖于语言的

    取决于底层语言定义的运行时结构。

    在通用语言实现中,根包括

    静态区域中的单词

    寄存器

    执行堆栈中指向堆的单词。

reachable

在free模式下管理内存,第一步 是区分可达对象和不可达对象

活对象与死对象

可以将对象和引用视为有向图:

    图表的活动对象是可从根目录访问的对象。

    执行计算的过程称为变化器,因为它在动态更改对象图。

跟踪可到达/无法访问的对象

我们可以简单地编码这个定义:

    第1步:从根开始; 现场设置为空

    步骤2:将根指向的任何对象添加到实时集

    第3步:重复

    将实时对象指向的任何对象添加到实时集

    直到找不到新的活动对象

    步骤4:不在实时集中的任何对象都是垃圾

如果一个对象是可达的,则为存活对象。

它被root中的对象指向或被live中的对象指向(从root间接指向)。

非活动对象称为死对象,即垃圾。

▪ GC的四种基本算法

识别垃圾并释放它占用的内存称为垃圾收集(GC)。

GC的成本指标

执行时间

    总执行时间

    GC执行时间的分配

    分配新对象的时间

内存使用情况

    额外的内存开销

    分裂

    虚拟内存和缓存性能

延迟时间

    破坏性暂停的时间长度

    僵尸时间

其他重要指标

    全面性

    实现简单性和健壮性

GC的主要任务是区分可达对象和不可达对象(存活对象与死亡对象)

四种方法:

引用计数:

    在每个对象上记下一个注释,指示对象的实时引用数。 如果对象的引用计数变为零,则抛出该对象(它已经死了)。

目标:确定您何时是唯一所有者,从而您可以做出处置决定。

基本思路:计算活动对象的引用数。

    每个对象都有一个引用计数(RC)

    复制引用时,引用的RC递增

    删除引用时,引用的RC将递减

    当RC = 0时,可以回收对象

    Recursive freeing 递归释放

    一旦对象的RC = 0,就可以释放它。

    但是对象可能包含对其他对象的引用。

    在释放此对象之前,还应释放其成分的RC。

标记-清除

    记下你需要的物体(根)。

    然后以递归方式标记活动对象所需的任何内容。

    之后,检查所有对象并丢弃没有标记的对象。

    通过跟踪活动对象的引用来查找其他活动对象。

    每个对象都有一个标志,标志是否属于live集。

    有两个阶段:

    标记阶段:

    从根开始,跟踪图形并在遇到的每个未标记对象中设置标记位。

    在标记阶段结束时,未标记的对象是垃圾。

    清除:从root开始,清扫堆

    未设置标记位:回收对象

    标记位置位:标记位清零

标记-压缩

    将标记放在您需要的物体上。

    将带有标记的任何东西移到库的最后。

    烧掉库前放面的一切(全是死内存)。

复制

    将您需要的物体移动到新库。

    然后递归移动新库中物体所需的任何物品。

    之后,烧毁旧库(其中的都已死)

    复制集合是一种更简单的解决方案:它挑选出活动对象并将它们复制到“新鲜”堆中

    复制垃圾收集

    将堆分成两半,称为半空间,命名为Fromspace和Tospace

    在Tospace中分配对象

    Tospace满员时

    翻转(翻转)半空间的角色

    选择Fromspace中的所有实时数据并将其复制到Tospace

    通过在Fromspace副本中保留转发地址来保持共享

    将Tospace对象用作工作队列

▪ Java/JVM的内存管理模型:各时期、各区域的GC方法

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

JVM会自动重新收集不再使用的内存。

    垃圾收集器将自动释放不再引用的对象的内存。

    要查看垃圾收集器开始工作,请将命令行参数“-verbose:gc”添加到虚拟机。

JVM中的垃圾收集

▪HotSpotVM(Sun JVM)有三个主要空间:年轻代,旧代和永久/元空间生成。

新对象分配到young generation中

GC后仍然存活的对象,提升到old generation中

PermGen/Metaspace中保存VM和class的元数据,以及类的静态变量

根据不同代的不同特征,采用不同的GC算法

In the young generation

每次GC会发现大量死亡对象,少量存活对象

复制算法适合,代价低

In the old generation

对象存活率高,适合采用标记算法

PermGen和Metaspace

PermGen中保存类的定义、静态方法、静态对象的引用等内容

缺点:

PermGen的容量是固定的(缺省或指定), 固定的容量容易导致运行时的内存溢出错误

Java 8 开始,用Metaspace替代了PermGen,区别在内存分配方面

Metaspace的容量是自动增长的,此外,当类的元数据使用达到了metaspace最大值时,会自动触发GC。

降低了OutOfMemory 的风险,但仍然需要监控和调优,避免内存泄漏

▪ JVM GC性能调优:参数配置、GC模式选择

GC中的性能注意事项:

吞吐量:总时间中非用于垃圾回收的时间

暂停: 由于GC导致的暂停次数

不同用户有不同的GC需求,E.g.,Web用户追求高吞吐量,而图形交互程序则追求低暂停。

scalability(可伸缩性). Promptness(及时性)

需要根据各种情况权衡代的容量,一个代的容量不影响其他代的回收频率和暂停时间。

没有普适的容量设置准则,应根据应用的内存使用和用户需求进行具体问题具体分析。最佳做法是将GC时间控制在执行时间的5%之内。

调整VM堆大小

JVM堆大小决定了虚拟机收集垃圾的频率和时间长短。

对于特定的程序,应分析实际情况后进行调整。

较大的堆,GC速度慢,GC频率低

如果根据内存需求设置堆大小,则完整垃圾回收速度会更快,但会更频繁地发生。

JVM有四种类型的GC实现:

    串行垃圾收集器

    并行垃圾收集器

    CMS垃圾收集器

    G1垃圾收集器

串行垃圾收集器

使用一个线程进行垃圾回收,执行时会冻结所有的应用线程,不适合多线程应用。

适合不要求低暂停时间和单机程序。

并行垃圾收集器

对年轻代的回收采用并行方式(多个线程) ,对老年代的回收还是单线程

它是JVM的默认GC,有时也称为吞吐量收集器。 但它在执行GC时也会冻结其他应用程序线程。

CMS垃圾收集器

利用多垃圾回收线程,适用于短回收暂停,且能够在应用程序运行时与垃圾收集器共享处理器资源。

应用程序平均响应较慢,但不会停止响应以执行垃圾收集。

G1垃圾收集器

适用于运行在多处理器大内存空间的应用程序。

▪ Java性能调优工具:jstat, jmap, jhat, Visual VM, MAT

jstat

获取JVM的heap使用和GC的性能统计数据

jstattool显示已检测的HotSpot Java虚拟机(JVM)的性能统计信息,重点关注内存性能,如堆使用情况和垃圾回收(GC)。

jmap

输出内存中的 对象分布情况

jhat

导出heap dump,浏览/查询其中的对象分布情况

jstack

获取Java线程的stack trace

jps (JVM Process Status Tool) 虚拟机进程状况工具

列出当前运行的JVM 进程

    jps命令使用java启动程序查找传递给main方法的类名和参数。

VisualVM是一种工具,它提供了一个可视化界面,用于查看有关Java应用程序在JVM上运行时的详细信息

它使用各种技术,包括jvmstat,JMX,Serviceability Agent(SA)和Attach API。

功能

显示本地和远程Java进程

显示流程配置和环境

监控流程性能和内存

    应用程序CPU使用率,GC活动,堆和元空间/永久生成内存,已加载类的数量和正在运行的线程。

    可视化流程线程

    在Java进程中运行的所有线程都会在时间轴中显示,同时显示聚合的

    Running,Sleeping,Wait,Park和Monitor时间。

    配置文件性能和内存使用

    提供采样和仪表分析仪。

    获取并显示线程转储

    帮助发现分布式死锁。

    获取并浏览堆转储

    帮助发现低效的堆使用情况并调试内存泄漏。

    在线和离线分析核心转储

    从核心转储中读取有关崩溃的Java进程及其环境的基本信息,以及提取和打开包含的线程和堆转储。

内存分析器(MAT)

Eclipse Memory Analyzer是一个快速且功能丰富的Java堆分析器,可帮助您查找内存泄漏并减少内存消耗。

使用Memory Analyzer分析具有数亿个对象的高效堆转储,快速计算对象的保留大小,查看谁阻止垃圾收集器收集对象,运行报告以自动提取泄漏嫌疑人。

▪ Java代码调优的设计模式:singleton, prototype/cloneable, flyweight, pool

Singleton Pattern单例模式

某些类在概念上只有一个实例

只创建一个对象,然后复用

对管理重用的代码进行封装

意图:确保类只有一个实例,并提供一个全局访问点

适用性:

只能有一个类的实例,并且客户端可以从众所周知的访问点访问它

当唯一的实例可以通过子类扩展时,客户端应该能够使用扩展实例而不修改它们的代码

好处

对唯一实例的受控访问

缩小命名空间(对全局变量的一种改进)

类通过封装确保对象的复用,不用让客户端考虑

节省创建时间

降低内存消耗

对于系统的核心组件和经常使用的对象,使用单例模式可以有效地提高系统性能。

Flyweight Pattern享元模式

享元模式描述了 如何共享对象,有效支持细粒度的使用而不会产生过高的成本

flyweight是一个共享对象,可在多个情境下同时使用

内在状态:不变的状态,可以共享

外在状态:状态是不固定的,使用时需要计算,不可共享

声明一个接口,flyweights可以通过该接口接收外部状态并对其进行操作

适用性:

应用程序使用了大量的对象,由于对象的数量庞大,存储成本很高

对象的大多数状态是外部状态,一旦删除了外部状态,可以用相对较少的共享对象替代很多组对象

应用程序不依赖于对象标识

Prototype Pattern 原型模式

通过复制已有原型对象新创建对象

目标:创建或初始化对象代价高时,可通过此模式创建相似对象,降低开销

类似于文档模板,创建一次,多次被复制使用,作为撰写文档的起点。

原型模式在初始化创建第一个对象时开销大,然后将这些值作为原型存储在存储库中。

需要再次创建相同或类似对象时,只需从存储库中获取所有值已经预填充的原型副本。

适用性:

当一个系统应该独立于它的产品创建、构成和表示时

当要实例化的类是在运行时刻指定时

为了避免创建一个与产品类层次平行的工厂类层次时

当一个类的实例只能有几个不同状态组合中的一种时,建立相应数目的原型并克隆它们,可能比每次用合适的状态手工实例化该类更方便一些。

Object Pool Pattern 对象池模式

对象池模式

▪对象池模式是一种软件创建设计模式,它使用一组准备使用的初始化对象 - “池” - 而不是按需分配和销毁它们。

▪例如,数据库连接池,打印机池

▪池的客户端将从池中请求对象并对返回的对象执行操作。

▪客户端完成后,将对象返回池而不是销毁它; 这可以手动或自动完成。

▪ 进程和线程

并发模块本身主要分为两种类型:进程和线程

进程是正在运行程序的一个实例,拥有自己私有专用的内存空间

线程是正在运行程序的一个执行路径(一个进程可对应多个线程),线程有自己的堆栈和局部变量,但是多个线程共享内存空间

进程可抽象为虚拟计算机,拥有独立的执行环境和完整的资源

进程间通常不共享内存

进程不能访问其他进程的内存或对象

需要特殊机制才能实现进程间共享内存

进程通信采用的是消息传递方式(因采用标准I/O 流)

进程通常被视为程序或应用程序的同义词,应用程序实际上可能是一组协作进程

为了实现进程间通信,大多数操作系统都支持”进程间通信(IPC)资源”,例如pipe和socket。

Java虚拟机本身的大多数实现都是作为单个进程运行的

线程可抽象为一个虚拟处理器,线程有时也称为轻量级进程

线程与进程中的其他线程共享相同的资源( 内存,打开的文件等),即“线程存在于进程内”

进程内的线程共享内存,线程需要特殊处理才能实现消息传递和访问私有内存

线程与进程

线程轻量级

进程重量级

线程共享地址空间

进程有自己的

线程需要同步

进程不需要

线程在改变对象时保持锁定

杀死线程是不安全的

安全进程是安全的

▪ 线程的创建和启动,runnable

每个应用程序至少有一个线程

站在程序员角度,main线程是开始线程,可以通过它创建其他的线程

启动线程的方法:

创建Thread类的子类

实现Runnable接口,作为参数传递给 new Thread(…)构造函数

所有的线程都需要实现Runnable接口,并实现run()方法

Runnable接口表示线程要完成的工作。

如何创建一个线程

▪方法1:子类线程

    Thread类本身实现了Runnable,尽管它的run方法什么都不做。 应用程序可以子类化Thread,提供自己的run()实现。

    ▪调用Thread.start()以启动新线程。

▪方法2:提供Runnableobject

    Runnable接口定义了一个方法run(),意味着包含在线程中执行的代码。 Runnable对象被传递给Thread构造函数。

    ▪调用Thread.start()以启动新线程。

    惯用法:用一个匿名的Runnable启动一个线程,它避免了创建命名的类

▪ 时间分片、交错执行、竞争条件

时间分片

在某时刻,一个运行核心上只有一个线程可以运行

进程/线程等采用OS提供的时间片机制共享处理时间

当线程数多于处理器数量时,并发通过时间片来模拟,处理器切换处理不同的线程。

时间片的使用是不可预知和非确定性的,这意味着线程可能随时暂停或恢复。

Interleaving 交错交叉/交错

低层的指令间存在交叉

Race Condition 竞争条件

竞争条件:程序的正确性(后置条件和不变性的满足)取决于并发计算A和B中事件的相对时间

在有些情况下会导致违反后置条件和不变性

▪ 线程的休眠、中断

▪ 线程安全的四种策略****

限制可变变量的共享

通过将数据限制在单个线程中,可以避免线程在可变数据上进行竞争

局部变量保存在线程栈中,每个调用都有自己的变量副本

局部变量如果是对象的引用,则要确保不能引用任何其他线程可访问的对象(针对可变对象)

在多线程环境中,取消全局变量

用不可变的共享变量

不可变解决了因为共享可变数据造成的竞争,并简单地通过使共享数据不可变来解决它

final变量不允许再赋值,所以声明为final的变量可以安全地从多个线程访问。

因为这种安全性只适用于变量本身,仍然必须确保变量指向的对象是不可变的。

将共享数据封装在线程安全的数据类型中

StringBuffer 和 StringBuilder都是可变数据类型,功能基本相同

StringBuffer是线程安全的,StringBuilder不是

StringBuilder性能更好,推荐在单线程程序中使用

使用同步机制来防止线程同时使用变量

并发模块彼此间采用同步的方式共享内存,以解决竞争带来的问题

锁是一种抽象,某时刻最多只允许一个线程拥有锁

获取锁的拥有权,如果锁被其他线程拥有,将进入阻塞状态,等待锁释放后,再同其他线程竞争获取锁的拥有权,锁拥有者释放锁的拥有权

同步语句必须指定提供内部锁的对象

同步区域提供了互斥功能: 一次只能有一个线程处于由给定对象的锁保护的同步区域中

锁定时,遵循顺序执行模式

▪ 死锁

由于使用锁需要线程等待(当另一个线程持有锁时),因此可能会陷入两个线程都在等待对方的情况 – 此时都无法继续

死锁描述了两个或更多线程永远被阻塞的情况,都在等待对方

死锁可能涉及两个以上的模块:线程间的依赖关系环是出现死锁的信号


防止死锁的一种方法是对需要同时获取的锁进行排序,并确保所有代码按照该顺序获取锁定

▪ Message passing

Message passing模型用于在客户端和服务器端的进程间通过sockets传递消息

在同一进程的线程间通过message passing传递消息,比通过锁定机制共享内存更受欢迎

使用同步队列在线程之间传递消息

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,874评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,102评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,676评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,911评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,937评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,935评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,860评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,660评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,113评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,363评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,506评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,238评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,861评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,486评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,674评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,513评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,426评论 2 352