JVM/多线程知识点

JVM/多线程知识点

  • 最好的学习方法是去看官方文档(English or China)
    • 最好的学习方法是去看官方文档(English or China)
      • 最好的学习方法是去看官方文档(English or China)
        • 1、java对于类复用的三种方式:组合、继承和代理
        • 2、多线程常用方法区别
        • 3、前期绑定和后期绑定
        • 4、java对象的内存布局
        • 5、类的加载
        • 6、JVM的运行模式
        • 7、引用:强引用>软引用>弱引用>虚引用
        • 8、Reactive响应式编程
        • 9、垃圾回收算法
        • 10、volatile关键字
        • 11、类加载到内存中的生命周期
        • 12、类加载器
        • 13、栈帧
        • 14、GC收集器
        • 15、运行时数据区域
        • 16、线程、工作内存、主内存之间的关系
        • 17、工作内存与主内存之间操作变量的8个动作
        • 18、线程安全程度
        • 19、线程安全的实现方法
        • 20、CAS机制(比较并替换)
        • 21、AQS(AbstractQueuedSynchronizer)
        • 22、JVM的锁优化
        • 23、幂等性
        • 24、在多线程中,代码不会按照编写顺序去执行
        • 25、Threadlocal(线程本地变量)
        • 26、java提供的容器
        • 27、Callable和Future
        • 28、线程池(ThreadPoolExecutor)
        • 29、内存屏障
        • 30、泛型
        • 31、序列化
        • 32、反射
        • 33、语法糖
        • 34、JVM优化
        • 35、面向对象五大基本原则
        • 36、Exception和Error的区别

最好的学习方法是去看官方文档(English or China)

最好的学习方法是去看官方文档(English or China)

最好的学习方法是去看官方文档(English or China)

1、java对于类复用的三种方式:组合、继承和代理

2、多线程常用方法区别

sleep()和wait()的区别:
1、sleep()方法是属于Thread类中的,而wait()方法是属于Object类
2、调用sleep()方法的过程中,线程不会释放对象锁。而调用wait()方法的时候,线程会放弃对象锁。
run()和start()方法区别:
run():仅仅是封装被线程执行的代码,直接调用是普通方法
start():首先启动了线程,然后再由jvm去调用该线程的run()方法

3、前期绑定和后期绑定

前期绑定:在程序执行前,将一个方法调用同一个方法主体关联起来称为前期绑定,这是面向过程语言(c语言)的默认绑定方式。
后期绑定(动态绑定):在程序执行中,将一个方法调用根据对象的类型进行绑定称为后期绑定,多用于java语言,当一个方法被声明为static或final时,不进行动态绑定。

4、java对象的内存布局

对象头、实例数据和对齐填充
对象头:对象头存储信息分为两部分,一部分是对象自身的运行时数据,例如:哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,另一部分是对象指向他的类元数据的指针
实例数据:对象真正存储的有效信息
对齐填充:无特殊作用,类似占位符(jvm需求对象的起始地址必须是8的整数倍)

5、类的加载

class对象仅在需要的时候才会被加载(例如new String()),static的初始化是在类加载时执行的

6、JVM的运行模式

1、Server模式:启动较慢,但是启动进入稳定期后运行速度比Client模式要快(启动了重量级虚拟机,基于C2编译器,优化更彻底)
2、Client模式:启动较快,但是启动进入稳定期后运行速度比Server要慢(C1·编译器)

7、引用:强引用>软引用>弱引用>虚引用

强引用:类似

Object obj = new Object()

只要强引用还在,垃圾收集器永远不会回收被引用的对象
软引用:描述有用但并非必要的对象,在发生内存溢出之前会将软引用对象列入回收范围进行二次回收,实现类SoftReference
弱引用:描述有用但并非必要的对象,只能存活到下一次垃圾回收之前,实现类WeakReference
虚引用:虚引用对象的生存时间不受引用的影响,只在对象被回收时收到一个系统通知

8、Reactive响应式编程

Reactive响应式(反应式)编程 是一种新的编程风格,其特点是异步或并发、事件驱动、推送PUSH机制以及观察者模式的衍生。reactive应用(响应式应用)允许开发人员构建事件驱动(event-driven),可扩展性,弹性的反应系统:提供高度敏感的实时的用户体验感觉,可伸缩性和弹性的应用程序栈的支持,随时可以部署在多核和云计算架构。

9、垃圾回收算法

①标记—清除算法:先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象
②标记—整理算法:先标记出所有需要回收的对象,将存活对象都向一端移动,然后直接清理掉端边界以外的内存
③复制算法:将可用内存按容量划分为大小相等的两块,每次只使用一块,当使用块内存用完时,就将存活的对象复制到另一块,然后清除使用块内存
④分代整理算法:将java内存划分为新生代和老年代(持久代已被消除),对象每熬过一次垃圾清理,代数加1,代数达到15后进入老年代,当内存不够用时对老年代进行标记—清除标记—整理算法进行回收
GC判断策略(何时判断此对象为垃圾):
①引用计数法(Reference Counting):给对象添加一个引用计数器,每过一个引用计数器值就+1,少一个引用就-1。当它的引用变为0时,该对象就不能再被使用。它的实现简单,但是不能解决互相循环引用的问题
②根搜索算法(GC Roots Tracing):以一系列叫“GC Roots”的对象为起点开始向下搜索,走过的路径称为引用链(Reference Chain),当一个对象没有和任何引用链相连时,证明此对象是不可用的,用图论的说法是不可达的。那么它就会被判定为是可回收的对象。
JAVA里可作为GC Roots的对象 :
虚拟机栈(栈帧中的本地变量表)中引用的对象
方法区中的类静态属性引用的对象
方法区中的常量引用的对象
本地方法栈中JNI(即Native方法)的引用的对象

什么情况下触发垃圾回收:
1.Scavenge GC:一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区
2.Full GC:当①年老代(Tenured)被写满②持久代(Perm)被写满 ③System.gc()被显示调用④上一次GC之后Heap的各域分配策略动态变化。对整个堆进行整理,包括Young、Tenured和Perm。Full GC因为需要对整个对进行回收,所以比Scavenge GC要慢,因此应该尽可能减少Full GC的次数。

10、volatile关键字

volatile底层实现:
当写一个volatile 变量时,JMM 会把该线程对应的本地内存中的共享变量值立即刷新到主内存中。
当读一个volatile 变量时,JMM 会把该线程对应的本地内存设置为无效,直接从主内存中读取共享变量
volatile关键字两个特性:
①可见性:被声明的变量对所有线程可见,任意一个线程修改了此变量的值,其他线程可以立即知晓(volatile 保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新
②禁止指令重排序优化(内存屏障):指令重排序时不能把后面的指令重排序到内存屏障之前的位置,只有一个CPU访问内存时,并不需要内存屏障
volatile关键字为何无法避免并发的情况发生:
多线程运行拥有各自线程的线程栈,线程栈从进程栈(主栈)中获取要计算的值保存到线程栈,然后在线程栈中进行运算后直接赋值给进程栈。volatile关键字可以保证读取时的数值统一,但是无法阻止多线程并行,例如:(线程1、2同时读取进程栈中的同一值对其进行运算,读取时为1,线程1对此值加1后赋值给主栈,此时值为2,线程2对此值加2后赋值给主栈,此时会将线程1的运算覆盖掉,此时主栈值为3),volatile关键字只能保证值从主栈读取到线程栈时是最新的,但是并不能保证在被读取后到被赋值之前值不会改变(即volatile只能保证可见性,但加锁可以保证可见性和原子性)。
synchronized不能禁止指令重排序,所以最好对变量加关键字volatile

11、类加载到内存中的生命周期

         加载、链接(验证、准备、解析)、初始化、使用、卸载,共7个阶段
加载:通过一个类的全限定名(类全名)来获取其定义的二进制字节流,将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构,在 Java 堆中生成一个代表这个类的 java.lang.Class 对象,作为对方法区
链接:验证类中的字节码,为静态域分配存储空间,解析这个类创建的对其他类的所有引用
验证:文件格式的验证、元数据的验证、字节码合法性验证、符号引用验证
准备:类变量(static)内存分配、按类型进行初始默认值分配(如0、null、false)(注意:如果类字段的属性表中存在ConstantValue属性,即同时被static和final修饰,那么在准备阶段该字段就会被初始化为指定的值,而不是初始默认值)
解析:虚拟机将常量池内的符号引用转换为直接引用的过程
初始化:如果该类具有超类,则对其初始化,执行静态初始化器和静态初始化块
内存分配的两种方式:
①指针碰撞:假设Java堆中的内存是绝对规整的,分为空闲和非空闲两种,中间用一个指针当做划分界限的指示器;当一个新对象需要分配对象时,相当于把指针向空闲区域移动一段与对象大小相等的距离
②空闲列表:假设Java堆的内存不是绝对规整的,空闲和非空闲是相互交错的,那就需要一个列表,用来记录哪些内存块是可以用的,在对象分配内存时,划分一块大小相等的区域给对象,并更新这个列表

12、类加载器

定义:根据指定全限定名称将class文件加载到JVM内存,转为Class对象
工作原理基于三个机制:委托、可见性和单一性
启动类加载器(Bootstrap ClassLoader):由C++实现,是虚拟机的一部分,负责加载%JAVA_HOME%/jre/lib,-Xbootclasspath参数指定的路径以及%JAVA_HOME%/jre/classes中的类文件,它是所有类加载器的父加载器。
扩展类加载器(Extension ClassLoader):负责加载%JAVA_HOME%/jre/lib/ext目录下或者java.ext.dirs系统属性定义的目录下的类
应用程序类加载器(Application ClassLoader):负责加载classpath环境变量中某些应用相关的类
父委派模型(双亲委派模型):当一个类加载器收到加载类的请求时,先查看自己是否加载过此类,如果没有,就委托父类加载器加载,当父类加载器无法加载时,再自己加载,如果自己也无法加载就抛出异常(好处:java类随着他的类加载器一起具备了一种带有优先级的层次关系

13、栈帧

定义:用于虚拟机进行方法调用和方法执行的数据结构(一个栈帧代表类中的一个方法)
包括:局部变量表、操作栈、动态连接、回调地址和一些额外的附加信息
局部变量表:存储了编译器存放着各种基本数据类型(8种基本类型)、对象引用reference类型,不等同于对象本身,根据不同的虚拟机实现,可能是一个指向对象起始地址的引用指针,也可能是一个代表对象的句柄或者其他与对象相关的位置)、returnAddress 类型(指向下一条字节码指令的地址)

14、GC收集器

四种GC收集器:
1、Serial(串行收集器):只使用一个线程进行回收,回收时会发生stop the World(最老的一种GC,适用于CPU个数较少且内存较小的场景)
2、CMS(并发收集器):使用空闲列表(free-lists)管理内存空间的回收,不对老年代进行碎片整理,可以减少用户线程暂停时间,但是老年代会有内存碎片。(适用于CPU个数较多、需要缩短停顿时间、提高响应速度的场景)
3、Parallel(并行收集器):可以实现多线程并行化GC操作,所以暂停时间较短,可实现可控的吞吐量与停顿时间(适用于高吞吐量但对暂停时间不敏感的场景,比如批处理)
4、G1:将堆空间划分为多个小模块而不是连续的年轻代和老年代,每个模块可能是Eden区/Survivor区/Old区(适用于FullGC(全局GC) 发生相对比较频繁或消耗的总时长过长、 对象分配率或对象升级至老年代的比例波动较大的场景)
常用的组合方式:

  1. 年轻代和老年代的串行收集器: Serial GC)
  2. 年轻代和老年代的并行收集器:(Parallel GC)
  3. 年轻代的并行收集器 (Parallel New) + 老年代的并发收集器(CMS Concurrent Mark and Sweep)
  4. 年轻代和老年代的 G1 收集器, 负责回收年轻代和老年代

15、运行时数据区域

运行时数据区域:程序计数器、java虚拟机栈、本地方法栈、java堆、方法区、运行时常量池
堆(java堆)和栈(java虚拟机栈)概括:
1 栈是运行时的单位 , 而堆是存储的单元。
2 栈解决程序的运行问题,即程序如何执行,或者说如何处理数据,堆解决的是数据存储的问题,即数据怎么放,放在哪儿。

16、线程、工作内存、主内存之间的关系

JVM/多线程知识点_第1张图片
JVM/多线程知识点_第2张图片

17、工作内存与主内存之间操作变量的8个动作

lock(锁定):作用于主内存的变量,它把一个变量标识为一条线程独占的状态
unlock(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才能被其他线程锁定
read(读取):作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用
load(载入):作用于主内存的变量,它把read操作获取的变量值放入工作内存的变量副本中
use(使用):作用于主内存的变量,它把工作内存中的变量值传递给执行引擎,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作
assign(赋值):作用于主内存的变量,它把从执行引擎接收到的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作
store(存储):作用于主内存的变量,它把工作内存中的变量值传递到主内存中,以便随后的write动作使用
write(写入):作用于主内存的变量,它把store操作获取的变量值放入主内存的变量中
8个动作必须满足的规则:
①不允许read和load、store和write操作之一单独出现,即不允许一个变量从主内存读取了但工作内存不接受,或者从工作内存发起回写了但主内存不接受的情况出现
②不允许一个线程丢弃他最近的assign操作,即变量在工作内存中改变了之后必须把该变化同步回主内存
③不允许一个线程无原因地(没有发生过任何assign操作)把数据从线程的工作内存同步回主内存中
④一个新的变量只能在主内存中诞生,不允许在工作内存中直接使用一个未被初始化的变量(即对一个变量实行use、store操作之前,必须先执行过了assign和load操作)
⑤一个变量在同一个时刻只允许一条线程对其进行lock操作,但lock操作可以被同一条线程重复执行多次,多次执行lock后,只有执行相同次数的unlock操作变量才会被解锁
⑥如果对一个变量执行lock操作,那将会清空工作内存中此变量的值,在执行引擎使用这个变量前,需要重新执行load或assign操作初始化变量的值
⑦如果一个变量事先没有被lock操作锁定,那就不允许对他执行unlock操作,也不允许去unlock一个被其他线程锁定的变量
⑧对一个变量执行unlock操作之前,必须先把此变量同步回主内存中(执行store、write操作)

18、线程安全程度

             不可变>绝对线程安全>相对线程安全>线程兼容>线程对立
①不可变:任何线程都无法改变此对象(final修饰),无论对于方法的实现还是方法的调用者而言此对象一定是线程安全的
②绝对线程安全:不管运行时环境如何,调用者都不需要任何额外的同步措施
③相对线程安全(常规意义上的线程安全):对此对象单独的操作是线程安全的,我们在调用的时候不需要做额外的保障措施,但是对于一些特定顺序的连续调用,就可能需要在调用端使用额外的同步手段来保障调用的正确性。
④线程兼容:此对象本身并不是线程安全的,但是可以通过在调用端正确的使用同步手段来保证对象在并发环境中可以安全的使用
⑤线程对立:无论调用端是否采取了同步措施,都无法在多线程环境中并发使用的代码

19、线程安全的实现方法

三种:加锁(互斥同步)、CAS策略(非阻塞同步)、Threadlocal保存共享数据
①互斥同步(阻塞同步/悲观策略):通过设置临界区、修改互斥量和信号量等手段保证多线程并发访问共享数据时,共享数据在同一时刻只被一个线程使用
②非阻塞同步(乐观策略):先进行操作,如果没有其他线程争用共享数据,则操作成功;如果共享数据有争抢,产生了冲突,就采取其他补救措施(例如:不停尝试,直到成功)
③无同步方案:
1.可重入代码:可以在代码执行的任何时刻去中断他转而执行另外一段代码
2.线程本地存储:如果共享数据的代码能保证在同一个线程中执行,可以把共享数据的可见范围限制在同一个线程中来实现线程安全

20、CAS机制(比较并替换)

CAS机制中使用了3个基本操作数:内存地址V,旧的预期值A,要修改的新值B
机制内容:更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为B
CAS机制的缺点:CPU开销大、不能保证代码块的原子性、ABA问题(如果内存地址V初次读取的值是A,在操作这个值的这段期间它的值曾经被改成了B,后来又被改回为A,那CAS操作就会误认为它从来没有被改变过。这个漏洞称为CAS操作的“ABA”问题。Java并发包为了解决这个问题,提供了一个带有标记的原子引用类“AtomicStampedReference”,它可以通过控制变量值的版本来保证CAS的正确性)

21、AQS(AbstractQueuedSynchronizer)

定义:抽象的队列式的同步器,AQS定义了一套多线程访问共享资源的同步器框架,许多同步类实现都依赖于它,如常用的ReentrantLock、Semaphore、CountDownLatch
实现:AQS维护一个状态码(state)和一个FIFO线程等待队列,当对象被占用时state+1,其他线程想要调用对象需要先判断state是否为0,不为0则进入FIFO线程等待队列去等待调用(当某线程已经占有该对象时,如果再次获取此锁,state会累加,即可重入锁)

22、JVM的锁优化

①自旋锁:自旋锁类似于互斥锁,如果在获取自旋锁时,没有任何执行单元保持该锁,那么将立即得到锁;如果在获取自旋锁时锁已经有保持者,那么获取锁操作将自旋在那里,直到该自旋锁的保持者释放了锁(自旋锁适用于保持锁时间比较短的情况
缺点:容易产生死锁、CPU开销过大
②锁消除:虚拟机即时编译器在运行时,对一些代码上要求同步但是被检测到不可能存在共享数据竞争的锁进行消除
③锁粗化:如果一系列连续操作都是对同一个对象反复加锁和解锁,甚至加锁操作是出现在循环体中的,那样的加锁和解锁会造成大量不必要的开销,这时可以将锁的范围扩大到整个操作序列的外部,这样就只需要加一次锁,避免不必要消耗(例如:将锁加在循环体中)
④轻量级锁:锁对象第一次被线程获取的时候,虚拟机会把对象设置为偏向模式,同时使用CAS操作把获取到这个锁的线程的ID记录在对象的Mark Word中,如果CAS操作成功,持有偏向锁的线程以后每次进入这个锁相关的同步块时,虚拟机都可以不再进行任何同步操作
⑤可重入锁:就是支持重进入的锁,它表示该锁能够支持一个线程对资源的重复加锁,ReentrantLock就是可重入锁(例如:线程A、B,A请求占用对象的锁成功后,锁状态+1,此时B请求占用对象锁,占用失败,锁状态不变,此时)

23、幂等性

定义:一次和多次请求某一个资源对于资源本身应该具有同样的结果(网络超时等问题除外)。也就是说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同。

24、在多线程中,代码不会按照编写顺序去执行

在没有同步的情况下,编译器、处理器,运行时安排操作的执行顺序可能完全出乎意料。

25、Threadlocal(线程本地变量)

定义:为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量(经常用来解决 数据库连接、Session管理等)

26、java提供的容器

同步容器:vector(加锁的ArrayList)和hashtable(加锁的hashmap)
并发容器:CopyOnWriteArrayList和ConcurrentHashMap

27、Callable和Future

1、Callable

public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

2、Future
定义:对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果

28、线程池(ThreadPoolExecutor)

四种常见线程池:
CachedThreadPool:可缓存的线程池,该线程池中没有核心线程,非核心线程的数量为Integer.max_value,就是无限大,当有需要时创建线程来执行任务,没有需要时回收线程,适用于耗时少,任务量大的情况。
SecudleThreadPool:周期性执行任务的线程池,按照某种特定的计划执行线程中的任务,有核心线程,但也有非核心线程,非核心线程的大小也为无限大。适用于执行周期性的任务。
SingleThreadPool:只有一条线程来执行任务,适用于有顺序的任务的应用场景。
FixedThreadPool:定长的线程池,有核心线程,核心线程的即为最大的线程数量,没有非核心线程

线程池参数:
①corePoolSize:核心池的大小,即创建线程池时默认创建多少个线程(用于等待调用)
②maximumPoolSize:线程池最大线程数,最多能创建多少个线程
③keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止
④unit:参数keepAliveTime的时间单位,有7种取值(天、时、分、秒、毫秒、微秒、纳秒)
⑤workQueue:一个阻塞队列,用来存储等待执行的任务
⑥threadFactory:线程工厂,主要用来创建线程
⑦handler:一种拒绝策略(
线程池满时的拒绝策略有四种:
1、AbortPolicy:不执行新任务,直接抛出异常,提示线程池已满。
2、DisCardPolicy:不执行新任务,也不抛出异常
3、DisCardOldSetPolicy:将消息队列中的第一个任务替换为当前新进来的任务执行。
4、CallerRunsPolicy:直接调用execute来执行当前任务

29、内存屏障

内存屏障分为两种:Load Barrier 和 Store Barrier即读屏障和写屏障
作用:①阻止屏障两侧的指令重排序;②强制把写缓冲区/高速缓存中的脏数据等写回主内存,让缓存中相应的数据失效
java的内存屏障有四种:LoadLoad,StoreStore,LoadStore,StoreLoad

30、泛型

注意点:由于泛型是编译时有效、运行时无效的一种类型,所以可以在运行时将不同数据插入泛型数组中(例如:List中插入数字类型(int),可以通过反射调用它的add方法来添加进list)
泛型限制:
1、不能使用泛型类型创建实例,例如:E object = new E();是错误的
2、不能使用泛型类型创建数组,例如:E[] object = new E[capacity];是错误的
3、静态环境下不允许类的参数是泛型类型
4、异常类不能是泛型类型
优点:
1、提高编程时的灵活性,更好实现代码的复用
2、提高程序运行时的性能
普通用法:

/*
*泛型类: class 类名<泛型>{}
*/
public class 类名<T>{
    private List<T> list;
    /* 泛型方法: 访问修饰符 <泛型> 方法返回值类型 方法名(形参){} */
    static List<T> getListByEntity(T entity){
    }
}
/**
* 泛型接口: interface 接口名<泛型>{}
*/
public interface 类名<T> {
    public T getBean();
}

高级用法:

List<? extends Object> list = new ArrayList<String>(); //指向对象的数据类型必须继承Object类
List<? supper String> list1 = new ArrayList<Object>(); //指向对象的数据类型必须是String的父类或String实现的接口

31、序列化

一般实现Serializable接口,如果需要序列化的元数据较少,可以实现Externalizable接口,重写writeExternal和readExternal方法来具体定义实例化哪些元数据

32、反射

创建class对象的三种方式:

		//类对象(字节码对象)
		Class<?> c1=Point.class;
		//通过全类名获取类对象(字节码对象)
		Class<?> c2=Class.forName("com.java.study.Point");
		//通过类的实例获取类对象(字节码对象)
		Point p1=new Point();
		Class<?> c3=p1.getClass();

33、语法糖

定义:编译期间以特定的字节码或者特定的方式对这些语法做一些处理,开发者可以直接方便地使用,简化了编程。
java语法糖:泛型、自动装箱、自动拆箱、foreach循环、变长参数、内部类、枚举类

34、JVM优化

1、将-Xms(初始堆内存)和-Xmx(最大堆内存)设置为同一值,以避免每次垃圾回收完成后JVM重新分配内存
2、选择适合自身服务器的垃圾回收器
3、响应时间优先的应用,年轻代的值尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择)。吞吐量优先的应用,年轻代的值尽可能的设置大,可能到达Gbit的程度。因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8CPU以上的应用
4、可以使用JDK自带的调优工具Jconsole或VisualVM来实时监控JVM,查看哪些方法大量占用CPU时间,哪些对象在系统中中数量最大(一定时间内存活对象和销毁对象一起统计),然后具体去优化

35、面向对象五大基本原则

1、单一职责原则:是指一个类的功能要单一,不能包罗万象。
2、开放封闭原则:一个模块在扩展性方面应该是开放的,而在更改性方面应该是封闭的。比如:一个网络模块,原来只服务端功能,而现在要加入客户端功能,那么应当在不用修改服务端功能代码的前提下,就能够增加客户端功能的实现代码
,这要求在设计之初,就应当将服务端和客户端分开,公共部分抽象出来。
3、替换原则:子类应当可以替换父类并出现在父类能够出现的任何地方。比如:公司搞年度晚会,所有员工可以参加抽奖,那么不管是老员工还是新员工,也不管是总部员工还是外派员工,都应当可以参加抽奖,否则这公司就不和谐了。
4、依赖原则:具体依赖抽象,上层依赖下层。假设B是较A低的模块,但B需要使用到A的功能,这个时候,B不应当直接使用A中的具体类: 而应当由B定义一抽象接口,并由A来实现这个抽象接口,B只使用这个抽象接口:  这样就达到了依赖倒置的目的,B也解除了对A的依赖,反过来是A依赖于B定义的抽象接口。
通过上层模块难以避免依赖下层模块,假如B也直接依赖A的实现,那么就可能造成循环依赖。  
5、接口分离原则:模块间要通过抽象接口隔离开,而不是通过具体的类强耦合起来

36、Exception和Error的区别

1、Error是指在正常情况下,不大可能出现的情况,绝大部分的Error都会导致程序(比如JVM自身)处于非正常的、不可恢复状态。既然是非正常情况,所以不便于也不需要捕获,常见的比如OutOfMemoryError之类,都是Error的子类
2、Exception又分为可检查(checked)异常和不检查(unchecked)异常,不检查异常就是所谓的运行时异常,类似 NullPointerException、ArrayIndexOutOfBoundsException之类,通常是可以编码避免的逻辑错误,具体根据需要来判断是否需要捕获,并不会在编译期强制要求。

参考书籍
1、深入理解Java虚拟机 JVM高级特性与最佳实践
2、百度

你可能感兴趣的:(看书学习)