JVM
Java虚拟机是Java EE平台的基础,它是中间件和应用程序被部署和裕兴的地方。
Jim向中间件软件和你的Java/Java EE程序提供:
①而简直形式的Java/Java EE程序运行环境
②一些程序功能特性和工具(IO基础设施,数据结果,线程管库,安全,监控等等)
③借助垃圾回收的动态内存分配和管理
Java线程堆栈是一个给定时间的快照,它能提供所有创建出来的Java线程的完整清单。
一个Java线程包括:
线程的名称,线程类型&优先级,Java线程ID,原生线程ID,Java线程状态和详细信息,Java线程栈跟踪,Java堆内存分解
不再引用和不再需要时两个不同的概念,不再引用是从虚拟机的角度看对象,而不再需要是从程序员角度看。在某些场合下,需要清洗的告诉虚拟机,那么对象才会不被引用到。
对象&对象引用
Person p1 = new Person();
p1是对象引用
将一个对象引用设为null,是表示让该引用不指向任何其它物理内存,仅此而已 .将一个引用设为null,都是业务逻辑的需要,而不是为了内存关系的需要。将对象引用设为null,对内存泄露是没有实际价值的。
Java进程内存, 指整个Java进程占用的内存。等于Java堆内存+Perm内存+本地内存:
进程大小是java堆内存、Perm内存、本地内存与加载的可执行文件和库所占用内存的总和。
1. Java堆内存,这是JVM用来分配java对象的内存。即通过-Xmx-Xms设置的用来分配给Java对 象的内存。当执行一句java分配对象的代码:new String();那么就是从这块内存进行分配 的。如果未指定最大的堆大小,那么该极限值由JVM 根据诸如计算机中的物理内存量和 该时刻的可用空闲内存量这类因素来决定。始终建议您指定最大的java 堆值
2. Perm内存(PermGen space的全称是Permanent Generation space,是指内存的永久保存区域), 即通过-XX:PermSize设置的内存,这块内存是虚拟机用来加载class字节码文件的内存。 这块内存在系统运行期一般比较固定。因为类文件是有限的。这里所说的一般,还有一些 不一般的情况,目前在有些面向方面的编程中,会动态进行代码织入29操作,即系统在运 行期间会修改或者增加字节码,这种情况下会导致类改变或者增加新的类,类需要重新 加载,如果持续地有新类产生,可能会导致这块内存一直增加,直到这块内存溢出.
3. Java进程本地内存,这是JVM 用于其内部操作的内存。JVM 将使用的本地内存数量取决 于生成的代码量、创建的线程、GC 期间用于保存java 对象信息的内存,以及在代码生成、 优化等过程中使用的临时空间。如果有一个第三方本地模块,那么它也可能使用本地内 存。例如,本地JDBC 驱动程序将分配本地内存。最大本地内存量受到任何特定操作系统 上的虚拟进程大小限制的约束,也受到用-Xmx 标志指定用于java 堆的内存量的限制。例 如,如果应用程序能分配总计为3 GB 的内存量,并且java 堆的大小设为1 GB,那么本地 内存量的最大值可能在2 GB 左右。即JVM使用的本地内存由如下几部分组成:
(a) Java.exe是C/C++写的程序,运行过程中自然需要内存,包括操作系统加载该程序, 和java.exe运行过程中自己分配的内存。
(b) JNI调用动态库使用的内存,即JNI中调用new或者malloc的内存等。
堆内存 Java虚拟机为Java对象(即Java代码中通过new操作符创建的对象)预留的空间。这块内存通过-Xmx和-Xms指定。
Perm内存 Java虚拟机为加载class预留的空间,这块空间通过-XX:PermSize指定。
本地内存 Java虚拟机本身是C/C++写的,运行期间自然也需要内存,另外如果系统 中有JNI调用,那么Native 代码中使用的内存也是这块。这块内存没有参数进行指 定。 这块内存主要是被JVM用作存放Class和Meta信息的,这些信息一经载入,就很少发生变 化,class就属于这种类型的数据,当Class在被Loader时就会被放到PermGen space中, 它和存放 类实例(Instance)的Heap区域不同,GC(Garbage Collection)不会在主程序运行期对PermGenspace进 行清理 。
当在Java中创建一个java线程的时候,同时也创建一个操作系统的 线程,而实际工作的是这个操作系统的线程。Java中的线程实际上是一个虚拟的。
并行垃圾回收,这里的并行相对于垃圾回收线程自己,即垃圾回收线程有多个,垃圾回收 线程是并行运行的。
并发垃圾回收,这里的并发是相对于Java应用程序而言的,垃圾回收和应用程序并发运行。
JVM命令行参数分为三种:
①标准的运行期参数
②-X扩展参数
③-XX扩展参数
JIT:允许实时将Java解释型程序自动编译成本地机器语言,以使程序执行的速度更快。有些JVM包含JIT编译器。
为了能做到平台无关,Java先把源代码编译成字节码,由JVM得interpreter转换成适合该平台的机器码。但解析运行很慢,于是把比较常用的bytecode先用JIT编译处理,下次遇到相同代码可直接调用以加快速度。
字节码解析很慢,JVM运行时解析器必须读取字节码译码,然后执行,循环往复,这个过程是非常消耗事件的,JIT就是为了解决这个。
JIT不是JVM的组成部分,但它是一个Java运行环境的一个标准组件。
正确看待虚拟机:
虚拟机实际上是一个程序,当程序启动时,就开始执行八寸在.class文件中的字节码指令。.class中的指令的执行是由虚拟机程序来执行的。.class相当于脚本
java.lang.StackOverflowError 出现这种情况是由于代码存在递归调用导致的调用层次太多,超过了系统的限制。通过观察异常堆栈,很容易发现问题所在。
如何计算GC消耗
过高的GC消耗会使系统变慢,同时暗示过多的创建和释放对象,需要进行优化。计算方法比较简单,可以扫描和分析文件,将所有GC条目消耗的时间除以应用运行的总时间即可,
公式为:GC时间和/系统运行时间
假设上例运行了2分钟即120秒,GC时间和为0.5225017秒,则GC占用CPU比率为0.5225017/120,约等于0.004,等于0.4%,因此GC消耗占了0.4%。
注意:系统在运行时间内并不是100%的占用CPU,偶尔也会有其他程序抢占CPU,可以通过一些操作系统工具来统计占用比例,假设实际上在2分钟内只占用了10%的CPU时间,则上例的GC消耗为:0.5225017/(120*10%),约等于4%,因此GC消耗实际占了4%
GC性能指标
参考一些JAVA性能调优网站上的指标,GC时间在低于5%的时候很理想,5%-15%为可接受,超过15%则被认为GC过于频繁,对象数量过多。
Java的自动垃圾回收只能保证内存被回收,不能保证资源被回收,两者是不同的概念。不同的资源回收方法不同,必须显式的调用资源的回收代码
Double-Checked Locking( 双检查锁)是一种非常广泛使用的一种代码模式,用在多线程场合下的懒初始化 (lazy initialization)
public synchronized MyInstance getInstance(){
if(this.instance == null){
this.instance = new MyInstance90;
}
return this.instance;
}
池的设计:常见的池有对象池,线程池,连接池;
对象池化得基本思路是:将用过的对象保存起来,等下一次需要这种对象的时候,再拿出来重复使用,从而在一定程度上减少频繁创建对象所造成的开销。只有重复生成某种大对象的操作成为影响性能的关键因素的时候,才适合进行对象池化。对一些小对象,使用池化技术不能带来任何性能的提升,反而会导致一定的性能下降。
Java问题定位技术
功能性问题定位属于简单的问题,可使用单步跟踪等方法
复杂问题定位需着重稳定性和可靠性。
1.线程堆栈 也称 作线程调用堆栈。
Java线程堆栈是虚拟机中线程(包括锁)状态的一个瞬间快照,即系统在某 个时刻所有线程的运行状态,包括每一个线程的调用堆栈,锁的持有情况等信息。每一种Java虚 拟机(SUN JVM、IBM JVM、JRokit、GNU JVM等等)都提供了线程转储(thread dump)的后门, 通过这个后门可以将那个时刻的线程堆栈打印出来。
线程堆栈的信息都包含:
? 线程的名字,ID,线程的数量等。
? 线程的运行状态,锁的状态(锁被哪个线程持有,哪个线程再等待锁等)。
? 调用堆栈(即函数的调用层次关系)。调用堆栈包含完整的类名,所执行的方法,源代码 的行数。 ?
Java线程实际上和本地线程指的是同一个东西,只有本地线程才是真正的线程实体,
Java线程实际上就是指这个本地线程,它并不是一个另外存在的的实体。
Java线程”runnable"表示当前线程处于运行状态。这个runnable状态是从虚拟机的角度来看的,表示这个线程正在运行。但是处于Runnable状态的线程不一定真地消耗CPU. 处于Runnable的线程 只能说明该线程没有阻塞在java的wait或者sleep方法上,同时也没等待在锁上面。但是如果该线程调用了本地方法7,而本地方法处于等待状态,这个时候虚拟机是不知道本地代码中发生了什么,此时尽管当前线程实际上也是阻塞的状态,但实际上显示出来的还是runnable状态, 这种情况下是不消耗CPU的 。
类的初始化
对象的初始化
当执行obj.wait(2000),当前线程正在被挂起,该线程当前不消耗CPU;
当执行Thread.sleep(2000),表示当前线程被挂起一段时间,该线程当前不消耗CPU;
线程死锁原因:
当两个或多个线程正在等待被对方占有的锁,死锁就会发生。
在Java事务管理中,三种可用的模型分别为 本地事务模型,编程式事务模型,声明式事务模型
本地事务模型:开发者管理的只是资源连接,而不是事务。真正管理本地事务的是数据库系统。
编程式事务模型:不鼓励,唯一使用的接口javax.transaction.UserTransaction
使用的方法:begin(),commit(),rollback(),getStatus()
声明式事务模型:只需在配置文件中配置,管理的是事务而不是连接
接口:TransactionManager
ACID指原子性Atomocity,一致性Consistency,隔离性Isolation,持久性Durability
JTS Java Transaction Service
JTA是一组可供开发者们管理事务的接口,JTS是JTA的底层事务实现者。关系类似于JDBC与底层数据库驱动。
JTA 等效于 JDBC API, 而 JTS 则等效于相应 的实现驱动。
Garbage-First,面向服务器的垃圾收集器,主要针对配置多处理器及大容量内存的机器,以极高概率满足GC停顿时间要求的同时,还具备高吞吐量性能特征。
可以像CMS收集器一样,GC操作与应用的线程一起并发执行
紧凑的空闲内存区且没有很长的GC停顿时间
需要可预测的GC暂停耗时
不想牺牲太多吞吐量性能
启动后不需要请求更大的Java堆
G1 vs CMS:G1是一款压缩型的收集器,G1通过有效的压缩完全避免了对细微空闲内存的分配,不用依赖于regions,大大简化了收集器,还消除了潜在的内存碎片问题。
G1执行垃圾回收的处理方式与CMS相似. G1在全局标记阶段(global marking phase)并发执行, 以确定堆内存中哪些对象是 存活的。标记阶段完成后,G1就可以知道哪些heap区的empty空间最大。它会首先回收这些区,通常会得到大量的自由空间. 这也是为什么这种垃圾收集方法叫做Garbage-First(垃圾优先)的原因。顾名思义, G1将精力集中放在可能布满可收回对象 的区域, 可回收对象(reclaimable objects)也就是所谓的垃圾. G1使用暂停预测模型(pause prediction model)来达到用户定 义的目标暂停时间,并根据目标暂停时间来选择此次进行垃圾回收的heap区域数量
被G1标记为适合回收的heap区将使用转移(evacuation)的方式进行垃圾回收. G1将一个或多个heap区域中的对象拷贝到其 他的单个区域中,并在此过程中压缩和释放内存. 在多核CPU上转移是并行执行的(parallel on multi-processors), 这样能减少 停顿时间并增加吞吐量. 因此,每次垃圾收集时, G1都会持续不断地减少碎片, 并且在用户给定的暂停时间内执行. 这比以前 的方法强大了很多. CMS垃圾收集器(Concurrent Mark Sweep,并发标记清理)不进行压缩. ParallelOld 垃圾收集只对整个堆 执行压缩,从而导致相当长的暂停时间。
需要强调的是, G1并不是一款实时垃圾收集器(real-time collector). 能以极高的概率在设定的目标暂停时间内完成,但不保证 绝对在这个时间内完成。 基于以前收集的各种监控数据, G1会根据用户指定的目标时间来预估能回收多少个heap区. 因此, 收集器有一个相当精确的heap区耗时计算模型,并根据该模型来确定在给定时间内去回收哪些heap区.
注意 G1分为两个阶段: 并发阶段(concurrent, 与应用线程一起运行, 如: 细化 refinement、标记 marking、清理 cleanup) 和 并行阶段(parallel, 多线程执行, 如: 停止所有JVM线程, stop the world). 而 FullGC(完整垃圾收集)仍然是单线程的, 但如果进 行适当的调优,则应用程序应该能够避免 full GC。
S Concurrent Mark Sweep并发标记清理收集器,回收年老代内存。