《深入理解Java虚拟机》读书笔记

一、晚期(运行期)优化

#栈空间大小 -xss
JDK1.7开始,栈空间默认为1M,JDK1.8同

#gc日志对应的参数列表

-XX:+PrintGC 输出GC日志
-XX:+PrintGCDetails 输出GC的详细日志
-XX:+PrintGCTimeStamps 输出GC的时间戳(以基准时间的形式)
-XX:+PrintGCDateStamps 输出GC的时间戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)
-XX:+PrintHeapAtGC 在进行GC的前后打印出堆的信息
-Xloggc:../logs/gc.log 日志文件的输出路径
#示例,sas的配置:
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintGCDateStamps
-XX:+PrintHeapAtGC
-Xloggc:/opt/wildfly/standalone/log/verbose.gc

#JIT编译
当编译工作完成后,这个方法的调用入口地址就会被系统自动改写成新的地址,下一次调用该方法是就会使用已编译的版本。
#主要的热点探测方式
基于采样的热点探测
基于计数器热点探测(方法调用计数器、回边计数器)
基于“踪迹”(trace)的热点探测
#OSR编译(On-Stack Replacement,栈上替换)回边计数器编译时采用
OSR是一种在运行时替换正在运行的函数/方法的栈帧的技术。但它是手段,不是目的——是出于某种目的需要在运行时替换栈帧。

#即时编译器优化技术一览
编译器策略
基于性能监控的优化技术
基于证据的优化技术
数据流敏感重写
语言相关的优化技术
内存及代码位置变换
循环变换
全局代码调整
控制流图变换

#方法内联(Method Inlining)
内联的主要目的有两个,一是去除方法调用的成本(如建立栈帧等),二是为其他优化建立良好的基础。
1.如果是非虚方法,编译器可以直接进行内联
2.如果是虚方法,引入“类型继承关系分析”(Class Hierarchy Analysis, CHA)技术,这是一种基于整个应用程序的类型分析技术,它用于确定在目前已加载的类中,
某个接口是否有多于一种的实现,某个类是否存在子类且子类是否为抽象类等信息。

#公共子表达式消除
如果一个表达式E已经被计算过了,并且从先前的计算到现在E中所有变量的值都没有发生变化,那么E的这次出现就成为了公共子表达式。
int d = (c * b) * 12 + a + (a + b * c);
int d = E * 12 +a + (a + E);
代数化简(algebraic simplification)
int d = E * 13 + a * 2;

#数组边界检查消除(Array Bounds Checking Elimination)
在Java语言中访问数组元素foo[i]的时候系统将会自动进行上下界的范围检查,即检查i必须满足i >=0 && I < foo.length的这个条件。
为了安全,数组边界检查肯定是必须做的,但数组边界检查是不是必须在运行期间一次不漏地检查则是可以“商量”的,比如可以在编译期根据数据流分析来确定foo.length的值,
并判断下标“3”没有越界,执行的时候就无需判断了。
类似的检查消除还有自动装箱消除、安全点消除、消除反射等。

#逃逸分析(Escape Analysis)
逃逸分析的基本行为就是分析对象动态作用域:当一个对象在方法里面被定义后,它可能被外部方法所引用,例如作为调用参数传递到其他方法中,这种行为称为方法逃逸。
甚至还有可能被外部线程访问到,譬如赋值给类变量或可以在其他线程中访问的实例变量,这种行为称为线程逃逸。
如果证明一个对象不会逃逸到方法或线程之外,则可能为这个变量进行一些高效的优化:
栈上分配(Stack Allocations)
同步消除(Synchronization Elimination)
标量替换(Scalar Replacement)

#垃圾收集器
#CMS收集器(Concurrent Mark Sweep)
初始标记(CMS initial mark)--应用挂起,会造成停顿
并发标记(CMS concurrent mark)
重新标记(CMS remark)--应用挂起,会造成停顿
并发清除(CMS concurrent sweep)
#G1(Garbage First)
是基于“标记-整理”算法实现的收集器,也就是说不会产生空间碎片。
G1将整个Java堆(包括新生代、老年代)划分为多个大小固定的独立区域(Region),并且跟踪这些区域里面的垃圾堆积程度,在后台维护一个优先列表,
每次根据允许的收集时间,优先回收垃圾最多的区域(这就是Garbage First名称的来由)。

#常量池
常量池之中主要存放两大类常量:字面量(Literal)和符号引用(Symbolic References)。
#字面量
字面量比较接近于Java语言层面的常量概念,如文本字符串、被声明为final的常量值等。
#符号引用
符号引用则属于编译原理方面的概念,包括下面三类常量:
类和接口的全限定名(Fully Qualified Name)
字段的名称和描述符(Descriptor)--描述符的作用是用来描述字段的数据类型、方法的参数列表(包括数量、类型以及顺序)和返回值
方法的名称和描述符

#访问标志(access_flags)
用于识别一些类或接口层次的访问信息,包括:这个Class是类还是接口;是否定义为public类型;是否定义为abstract类型;如果是类的话,是否被声明为final,等等。
#类索引、父类索引与接口索引集合
#字段表集合
#方法表集合
访问标志(access_flags)、名称索引(name_index)、描述符索引(descriptor_index)、属性表集合(attributes)几项。
注:方法内的代码,存放在方法属性集合中一个名为“Code”的属性里面。
#属性表集合

#动态连接
每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。


#Thread类中那么多native方法,了解为什么
Sun的解释器是用C实现的,这使得它能像一些普通的C一样与外部交互。jre大部分是用java实现的,它也通过一些本地方法与外界交互。
例如:类java.lang.Thread 的 setPriority()方法是用java实现的,但是它实现调用的是该类里的本地方法setPriority0()。这个本地方法是用C实现的,并被植入JVM内部,在Windows 95的平台上,这个本地方法最终将调用Win32 SetPriority() API。这是一个本地方法的具体实现由JVM直接提供,更多的情况是本地方法由外部的动态链接库(external dynamic link library)提供,然后被JVM调用。
总的来说,JAVA的native方法适用的情况:
1、为了使用底层的主机平台的某个特性,而这个特性不能通过JAVA API访问。
2、为了访问一个老的系统或者使用一个已有的库,而这个系统或这个库不是用JAVA编写的。
3、为了加快程序的性能,而将一段时间敏感的代码作为本地方法实现。

#volatile
1.保证此变量对所有线程的可见性,即一条线程修改了这个变量的值,新值对于其他线程来说是可以立即得知的。
注意,volatile变量在并发时也可能存在不一致的情况(如i++,这行代码对应4条字节码指令,高并发时会造成i值不一致)
2.禁止指令重排序优化
volatile变量赋值后,多执行了一个“lock add1 $0x0, (%esp)”,相当于内存屏障(Memory Barrier)
由于volatile变量只能保证可见性,在不符合以下两条规则的运算场景中,我们仍然要通过加锁(使用synchronized或java.util.concurrent中的原子类)来保证原子性:
(1).运算结果并不依赖变量的当前值,或者能够确保只有单一的线程修改变量的值;
(2).变量不需要与其他的状态变量共同参与不变约束。



 

posted on 2020-01-17 15:49  阿泰555  阅读(97)  评论(0编辑  收藏  举报

导航