Java虚拟机学习记录(九)——类文件结构(上)

##一、前言
在Java开发中,可以通过javac将.java的源代码编译为.class的类文件。之前一直以为,只有java语言可以编译为.class。但是在前些天的学习中,了解到不仅仅是Java语言可以编译成.class文件然后运行在Java虚拟机上,Clojure、Groovy、JRuby、Jython、Scala等语言都可以运行在Java虚拟机上。觉得这真是太神奇了。今天这一章的内容刚好可以解释这些。

##二、Java虚拟机的无关系特点
一般都说Java是平台无关的,因为Java是运行在Java虚拟机上的,而Java虚拟机是开发成各个平台通用的。那么同时,Java虚拟机其实也是语言无关的,也就是说,Java虚拟机并不要求特定的语言。只要该语言可以被编译生成符合标准的class(类)文件,那么就是可以运行在Java虚拟机上了。那么,类文件的结构是什么样的呢?

##三、类文件的基本知识
##1.基本单位
类文件是以8位字节为基础单位的二进制流,没有任何分割符。当遇到需要占用8位以上的数据时,则会按高位在前的方式分割成若干个8位字节进行存储。
###2.存储数据的数据格式:无符号数和表。
无符号数属于基本数据类型。以u1,u2,u4,u8来分别代表1个字节,2个字节,4个字节,8个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者安装UTF-8编码构成的字符串值。
表是由多个无符号数或者其他表作为数据项构成的复合数据类型,所有表都习惯性以_info结尾。

##三、类文件的结构
###1.魔数和版本号
class文件的前四个字节称为魔数(Magic Number),用来描述文件的格式,class文件的魔数是0xCAFEBABE。第五六个字节描述次版本号(Minor Version),第七八个字节描述主版本号。
###2.常量池
紧接着主版本号之后是常量池的入口。由于常量池的常量数量是变化的,所以在常量池的入口有一个u2类型(第9,10位)的数据,代表常量池容量计数值。不过这个计数是从1开始的,所以如果这个值是22,则代表有21个常量。
常量池中主要有两种类型:字面量(Literal)和符号引用(Symbolic References)。

a.字面量: 接近java语言的常量的概念,如文本字符串,声明为final的常量值等
b.符号引用:1.类和接口的全限定名(Fully Qualified Name) 2.字段的名称和描述符(Descriptor) 3.方法的名称和描述符

java代码在编译时不会像c/c++一样进行”连接”,这样在编译生成的class文件中不会保存各个方法、字段的最终内存布局信息,而是在运行的时候进行动态连接。也就是从常量池中获得方法、字段对应的符号引用,再在类创建或运行时解析翻译到具体的内存地址之中。

常量池中每一个常量都是一个表,在JDK1.7前共有11中常量,在JDK1.7中为了更好的支持动态语言调用,又额外增加了3种(CONSTANT_MethodHandle_info、CONSTANT_MethodType_info和CONSTANT_InvokeDynamic_info)。

这14个表的共同之处在于,表开始的第一项是一个u1类型的标志位(tag),代表当前这个常量属于哪种常量类型。

类型 标志 描述
CONSTANT_Utf8_info 1 UTF-8编码的字符串
CONSTANT_Integer_info 3 整型字面量
CONSTANT_Float_info 4 浮点型字面量
CONSTANT_Long_info 5 长整型字面量
CONSTANT_Double_info 6 双精度浮点型字面量
CONSTANT_Class_info 7 类或接口的符号引用
CONSTANT_String_info 8 字符串类型字面量
CONSTANT_Fieldref_info 9 字段的符号引用
CONSTANT_Methodref_info 10 类中方法的符号引用
CONSTANT_InterfaceMathodref_info 11 接口中方法的符号引用
CONSTANT_NameAndType_info 12 字段或方法的部分符号引用
CONSTANT_MethodHandle_info 15 表示方法句柄
CONSTANT_MethodType_info 16 表示方法类型
CONSTANT_InvokeDynamic_info 18 表示一个动态方法调用点

###3.访问标志
在常量池结束之后,紧接着的是访问标志,用来描述一些类和接口的访问信息,包括这个Class是类还是接口,是否定义为public,是否是abstract如果是类的话,是否是final。

###4.类索引,父类索引与接口索引集合
访问标志之后是类索引,父类索引与接口索引集合。类索引(this_class)和父类索引(super_class)都是一个u2类型的数据,而接口索引集合(interfaces)是一组u2类型的数据集合(对应java语言中的单继承和多接口实现),

###5.字段表集合
再之后是字段表集合。字段表(Field_info)用于描述接口或者类中声明的变量。字段(field)包括类级变量以及实例级变量,但不包括在方法内部声明的局部变量。一个字段的描述包括:字段的作用域(public,private,protected修饰符)、是实例变量还是类变量(static修饰符)、可变性(final)、并发可见性(volatile修饰符,是否强制从主内存读写)、可否被序列化(transient修饰符)、字段数据类型(基本类型,数组,对象)、字段名称。

###6.方法表集合
字段表之后是方法表集合。很显然,对方法的描述和对字段的描述是很像的。volatile和transient不能描述方法,但是syncronized、native、strictfp和abstract是方法独有的。

Java虚拟机学习记录(八) —— 虚拟机性能监控与故障处理工具

一、前言

我觉得Java的强大之处在于它有非常完善的生态环境,从开发工具到分析处理工具。使用JDK中提供的工具可以在遇到程序故障时快速定位故障发生的原因并进行调优。

二、JDK命令行工具

a、jps(JVM Process Status):虚拟机进程状况工具

jps可以用来列出正在运行的虚拟机进程,并显示虚拟机执行主类(Main class,main函数所在的类)名称以及这些进程的本地虚拟机唯一ID(Local Virtual Machine Identifier,LVMID)。

jps命令格式:
jps [option] [hostid]
使用范例:
jiang@jiang-HP-ENVY-Notebook:~$ jps -l
6084 /home/jiang/eclipse//plugins/org.eclipse.equinox.launcher_1.3.200.v20160318-1642.jar
6152 sun.tools.jps.Jps
jps可以通过RMI协议查询开启了RMI服务的远程虚拟机进程状态,hostid为RMI注册表中注册的主机名。

选项 作用
-q 只输出LVMID,省略主类的名称
-m 输出虚拟机进程启动时传递给主类main()函数的参数
-l 输出主类的全名,如果进程执行的是Jar包,输出Jar路径
-v 输出虚拟机进程启动时JVM参数

b、jstat(JVM Statistics Monitoring Tool): 虚拟机统计信息监视工具

jstat是用于监视虚拟机各种运行状态信息的命令行工具。它可以显示本地或者远程虚拟机进程中的类加载、内存、垃圾收集、JIT编译等运行数据。

jstat命令格式为:
jstat [option vmid [interval [s|ms] [count]] ]
如果VMID是本地进程,和LVMID是一样的,如果是远程虚拟机进程,那VMID的格式是:
[protocol:][//]lvmid[@hostname[:port]/servername]
参数inerval和count代表查询间隔和次数,如果省略则表示只查询一次。
使用范例:
jstat -gc 2764 250 20
代表每250ms查询一次进程2764垃圾收集状况,一共查询20次

选项 作用
-class 监视类装载,卸载数量,总空间以及类装载所耗费的时间
-gc 监视Java堆状况,包括Eden区,两个survivor区,老年代,永久代等的容量、已用空间、GC时间合计等信息
-gccapacity 监视内容与-gc基本相同,但输出主要关注Java堆各个区域使用到的最大、最小空间
-gcutil 监视内容与-gc基本相同,但输出主要关注已使用空间占总空间的百分比
-gccause 与-gcutil功能一样,但会额外输出导致上一次GC的原因
-gcnew 监视新生代GC状况
-gcnewcapacity 监视新生代使用到的最大、最小空间
-gcold 监视老年代GC状况
-gcoldcapacity 监视老年代使用到的最大、最小空间
-gcpermcapacity 输出永久代使用到的最大、最小空间
-compiler 输出JIT编译器编译过的方法、耗时等信息
-printcompilation 输出已被JIT编译的方法

Java虚拟机学习记录(七)——内存分配与回收策略

一、前言

Java的内存分配,从全局来看,就是堆上分配(但也可能经过JIT编译后被拆散为标量类型并间接的栈上分配,对象的分配主要在新生代的Eden区上,如果开启了本地线程分配缓冲,将按线程优先在TLAB上分配。少数情况下也可能会直接分配在老年代中(大的对象直接进入老年代)。

二、对象优先在Eden分配

Java堆的新生代中,被分为Eden和两个survivor。大多数情况下,对象在Eden区优先分配,当Eden区没有足够的空间进行分配时,虚拟机将会发起一起Minor GC

新生代GC(Minor GC):指发生在新生代的垃圾回收动作。
老年代GC(Major GC/Full GC):指发生在老年代的GC。

三、大对象直接进入老年代

大对象指的是要占用大量连续内存空间的Java对象,最典型的大对象就是那种很长的字符串以及数组。大对象的内存分配堆虚拟机来说是一件很难的事,因为往往会因为找不到这样的连续的内存空间而不得不提前触发一次Full GC。因此,在写程序中要尽量避免这样大对象,尤其是生命周期很短的大对象。大对象的分配一般会直接进入老年代。(这里可以认为JVM是很不支持生命周期很短的大对象的创建的)。

四、长期存活的对象将进入老年代

虚拟机为每个对象定义了一个年龄计数器,在一次GC后仍然存活的对象它的年龄就会+1,如果对象在Eden出生并且经过第一次Minor GC仍然存活并且Survivor能够容纳就会被移到Survivor(其实就是标记-复制法)。当它的年龄达到一定的程度(默认为15岁),就会被移入老年代。可以通过-XX:MaxTenuringThreshold来设定这个年龄阈值。

五、动态年龄判定

为了更好地适应不同程序的内存情况,虚拟机并不是永远都要求对象的年龄必须达到了MaxTenuringThreshold才能进入老年代,如果在Survivor区中相同年龄所有对象的大小总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代(节省了一半以上的Survivor内存)。

六、空间分配担保

在新生代的Minor GC是采用的标记——复制法,所以一次Minor GC后会将存活的对象移到另一个空闲的Survivor区中,但是没有人可以保证一次GC后存活的对象是Survivor能够容纳的,那么就需要老年代的进行空间分配担保,以防止在容纳不下的情况下有后备的解决方案。所以过程是:

Minor GC发生之前:检查老年代的连续空间是否能够容纳下新生代的所有对象,如果可以,则可以确保Minor GC是正常的。如果不可以,虚拟机就会检查HandlePromotionFailure设置值是否允许担保失败。如果允许,那么就会检查老年代最大可用连续空间是否大于历次进入老年代对象的平均大小,如果大于,则尝试一次Minor GC;如果小于,或者不允许担保失败,就要进行一次Full GC(所以可以理解为Full GC是为了给Minor GC腾出空间,所以Full GC之后往往跟随着一次Minor GC)。

Java虚拟机学习记录(六)——HotSpot算法实现

一、前言

在JVM运行的过程中,垃圾回收是性能提升的重中之重,垃圾回收的前提是准确判定哪些对象是可以回收的,在前面的学习中说到,Java的大多数虚拟机都是通过可达性分析算法来判定对象能否回收。那么如何去找这些根节点(GC ROOTS)的位置也是一个要解决的问题。

二、HotSpot枚举根节点(GC Roots)

根节点主要在全局性的引用(常量和类的静态属性)和执行上下文中(例如栈帧的本地变量表中),因为这些区域的占用内存往往很大,不可能去逐个检索,因为这个操作太耗时了。

同样的,可达性分析的时间严格要求还体现在GC停顿上,GC停顿就是指在可达性分析期间所有的Java执行线程得全部停下来,等分析完成之后再开始重新运行,如果不停顿,就可能导致分析期间,引用关系还在不断的改变。很显然,这个停顿时间必须短不然用户体验极差。Sun把这个停顿叫做Stop the world。

HotSpot采用一种叫OopMap的数据结构来记录所有的执行上下文和全局引用位置,这样就可以直接得到所有的GC Roots的地址了。

但是在Java程序运行过程中,有很多指令会导致对象的引用关系发生变化,如果每个变化都要写入到OopMap中,那样就需要维护一个很大的OopMap数据结构,会占用大量的空间。所以在Jvm中有一个安全点(SafePoint)的概念。HotSpot只在安全点处记录了这些信息,然后开始GC。安全点的选定不能太少造成GC等待时间过长,也不能频繁GC增加运行负荷。

如何让所有线程跑到安全点时停止下来,有两种方案可以选择,抢先式中断(Preemptive Suspension)主动式中断(Voluntary Suspension)

其中抢先式中断不需要线程的执行代码主动配合,在GC发生时,首先让所有线程中断,如果发现线程中断的地方不在安全点上,就恢复线程,让它跑到安全点上。现在几乎没有虚拟机实现采用抢先式中断来暂停线程从而响应GC事件。

主动式中断的思想是当GC需要中断线程的时候,不直接对线程操作,仅仅简单地设置一个标志,各个线程去主动轮询这个标志,发现中断标志为真时就自己中断挂起。轮询标志的地方和安全点是重合的,另外加上创建对象需要分配内存的地方。

但是安全点只能很好的解决运行中的程序,对于”不运行”的程序也就也就无法进入安全点,也就无法进行GC。这里的不运行指的是线程没有分配到CPU时间,典型的例子就是线程处于Sleep状态或者Blocked状态,这时候线程是无法响应JVM的中断请求的,”走”安全的地方去中断挂起,JVM显然也不太可能等待线程重新被分配CPU时间。这种情况需要借助安全区域(Safe Region)来解决。

安全区域指的是在一段代码中,引用关系不会发生变化。在这个区域任意开GC都是安全的。我们也可以把安全区域当成是安全点的扩展。

当线程执行到安全区域中时,首先标识自己进入了安全区域。在这段时间内JVM发起GC就不用管安全区域里的线程了。但是在安全区域内的线程重新获得CPU时间要离开Safe Region时,要检查系统是否已经完成了根节点枚举。完成了才能离开否则就要等待完成。

##三 HotSpot垃圾收集器的实现

a.Serial收集器

这是一款最基本,发展历史最悠久的收集器。这个收集器是一个单线程收集器,并且在收集垃圾时,必须暂停其他所有工作线程。适用于作为Client模式下的虚拟机。

b.ParNew收集器

其实就是Serial收集器的多线程版本。在单CPU的环境中,性能比不上Serial收集器,但是多CPU的时候性能是要好过Serial收集器的。

c.parallel Scavenge收集器

新生代收集器,复制算法,这个收集器与其他收集器的区别在于,Parallel Scavenge收集器的目标是达到一个可控制的吞吐量。吞吐量=运行用户代码所花费的时间/(运行用户代码时间+垃圾收集时间)。而其他收集则是关注减少GC停顿的时间。

d.Serial Old收集器

是Serial收集器的老年代版本,使用标记整理算法。也是给Client模式下的虚拟机使用。在server模式下,还有两大用途:1.在JDK1.5之前和Parallel Scavenge配合使用; 2作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure失败时使用。

e.Parallell Old收集器

是Parallel Scavenge收集器的老年代版本,使用多线程和标记-整理算法。配合paraller Scavenge使用。

f.CMS收集器

Concurrent Mark Sweep。以获取最短时间停顿为目标,基于“标记-清除”算法。包括四个步骤:
1.初始标记
2.并发标记
3.重新标记
4.并发清除
他有一下几个缺点:
1.对CPU资源敏感,对性能影响大
2.无法清理浮动垃圾
3.容易产生内存碎片,触发Full GC。

g.G1收集器

是一款面向服务器的垃圾收集器。有以下特点:
1.并行和并发
2.分代收集
3.空间整合
4.可预测的停顿

Java虚拟机学习记录(五)-垃圾收集器

一、前言

众所周知,Java与C的一个显著的区别在于c需要手动的去管理内存,而Java几乎不需要去做这样的处理。原因在于Java的虚拟机有一套自己的内存回收策略。

在Java虚拟机运行过程中,虚拟机栈、程序计数器、本地方法栈随着线程的生命周期的变化而变化,因此这一部分的内存是不需要额外的去回收。但是对于Java堆来说,几乎所有的对象的创建(这里之所以说几乎,是因为随着JIT编译器、对象逃逸分析和栈上分配的技术发展,部分对象不需要在堆中分配内存)都是在堆中进行,如果不作内存回收,很快就会被占满内存,那么怎么样去分析得到那些对象已经不再需要则是问题的关键。所以,要实现垃圾回收,先要判断对象是否已死,然后再对已死对象执行回收算法。下面记录几种常见的在垃圾回收中的算法

二、对象是否已死的判定——引用计数法

这种方法原理很简单,就是说每个对象增加一个引用就给他的计数器加1,当引用失效时(这里的引用失效,我觉得是说当程序的执行离开了对象的作用域,那么这个引用就算是失效了),计数器就减一。当计数器再次变为0时,这个对象就判定它已经可以回收了。

但是这种方法也有一个严重的问题,当ObjectA.instance = ObjectB;ObjectB.instance = ObjectA时,这里两个对象互相引用着导致引用计数一直不为0。因此在JVM的主流实现中,都不采用这种方法。据说python的GC算法就是引用计数加上辅助算法完成的。

三、对象是否已死的判定——可达性分析算法

可达性算法是借助树的数据结构的一个算法,通过判定一个对象是否可以通过树的根到达来确定其是否需要回收。引用一张来自网上的图片图片引用地址

可达性分析

在这里,虽然object 8,object 9,object 10,object 11,object 12都互相持有引用,但是因为从GC Roots中无法到达,所以可以判定为可回收对象。很好的解决了引用计数法的弊端。
在Java中,可作为GC Roots的对象包括:
虚拟机栈(栈帧中的本地变量表)中的引用对象
方法区中类静态属性引用的对象
方法区中常量引用的对象
本地方法栈中JNI引用的对象

四、垃圾回收算法——标记-清除算法

标记-清除算法共有两个阶段。标记和清除

标记是将内存空间扫描一遍,对所有可回收的对象做标记。清除是对内存空间再做一遍扫描,清除可回收的对象。这种方法有两个问题:一是要做两遍扫描,效率不高,二是会产生大量的内存碎片(回收的对象随机分布造成),当分配一个很大的对象时很有可能找不到这样的连续空间而提前触发一次垃圾回收。因此这种算法大多数的JVM的实现都不使用。

五、垃圾回收算法——复制算法

复制算法最大的特点是将内存空间分为大小相同的两部分。当开始垃圾回收时,只需将存活的对象移到另一块没有使用的内存空间,然后将使用的一边全部回收,这样的做法简单高效,但是对内存的浪费实在是太大了。就像是你买了8G的内存条只能使用4G,你肯定是不愿意的。

但是实际上,现在的商业虚拟机都采用这种算法的优化版本来回收新生代。因为在新生代中(Java堆会分为新生代[Young Generation]和年老代[Old Generation])中的对象绝大多数都是可回收的,那么实际上每次做垃回收时,新生代中存活的对象并不多。所以并不需要按照1:1进行空间分割,一般情况下,会将新生代分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden空间和一块Survivor空间,当进行垃圾回收时,将这两块空间中的存活对象移到另一块空闲的Survivor空间。然后将对象全部清除。这样只有10%的内存会被浪费。

经历了几次垃圾回收都依然存活的对象会被放置到年老代中,因此年老代中的对象都是不容易被回收的。

因为没有办法保证每次的垃圾回收过程存活的对象都不超过10%,所以当Survivor空间不够用时,需要依赖其他内存(这里指年老代)进行分配担保(Handle Promotion)。

六、垃圾回收算法——标记-整理法

上面的复制算法很明显不适用于年老代,因为年老代中的对象特点是存活率大。标记-整理算法与标记-清除算法类似,但是它不是直接对对象进行清除,而是将存活的对象向一端移动,然后直接清理掉端边界意外的内存。引用一张来自网络上的图片图片引用地址

此处输入图片的描述

七、扩展一下引用的知识

在四、五的判定对象是否已死中,均涉及到了引用。在上面的表述中,似乎只有引用和死亡两种状态。但是事实上,Java规定了四种引用状态来帮助Java程序获得更好的性能。怎么去理解这个,一般来说,当Java虚拟机的内存足够时,有的对象虽然已经不需要了,但是完全没有必要将它丢弃,只有在进行垃圾回收后内存仍然不足时才将这些对象回收。这样就增加了这些对象的复用。免去了下一次使用这些对象又要重新创建的问题。很多系统的缓存功能都符合这样的应用场景。

在JDK1.2后,Java扩充了引用的概念,分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)。

a.强引用
Object obj = new Object()这种就是强引用,只要这个引用存在,无论如何JVM都不会回收这些对象

b.软引用
软引用用来描述一些还有用但并非必需的对象。用软引用的对象在系统进行过垃圾回收仍然内存不足时才会进行回收。在JDK1.2之后,提供了SoftReference类来实现软引用。

c.弱引用
弱引用也是用来描述非必需的对象,但是强度比软引用还弱一点。在下一次GC时必定会回收。

d.虚引用
虚引用对对象的生存时间构不成影响,就和没有引用与之关联一样,所以任何时候的GC都会将其回收。对一个对象设置虚引用的唯一目的就是这个对象被回收时会收到一个系统通知。在JDK1.2以后,提供了PhantomReference类来实现虚引用。

用代码来检验一下软引用、弱引用和虚引用。

package test;

import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;

public class TestReference {
    public static boolean run = true;
    public static void main(String[] args){
        WeakReference<String> weakReference = new WeakReference<String>(new String("weak Reference"));
        SoftReference<String> softReference = new SoftReference<String>(new String("soft Reference"));
        final ReferenceQueue<String> queue = new ReferenceQueue<String>();
        new Thread(new Runnable() {

            @Override
            public void run() {
                while(run){
                    Object object = queue.poll();
                    if(object != null){
                        try {
                            Field referent = Reference.class.getDeclaredField("referent");
                            referent.setAccessible(true);
                            Object result = referent.get(object);
                            System.out.println("即将回收"+result.getClass()+(String)result);
                        } catch (NoSuchFieldException | SecurityException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        } catch (IllegalArgumentException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }

            }
        }).start();
        PhantomReference<String> phantomReference = new PhantomReference<String>(new String("phantom Reference"), queue);

        System.out.println(weakReference.get());
        System.out.println(softReference.get());
        System.out.println(phantomReference.get());
        System.out.println("*****************下面开始GC******************");
        System.gc();        //System.gc只是建议系统进行垃圾回收,并不是立刻执行
        System.out.println(weakReference.get());
        System.out.println(softReference.get());
        System.out.println(phantomReference.get());
    }
}

上面这段代码的输出为

weak Reference
soft Reference
null
*****************下面开始GC******************
null
soft Reference
null
即将回收class java.lang.Stringphantom Reference

可以看到,软引用在gc之后仍然是存在的,而弱引用gc之后变成null了,虚引用一直为null。并且我们通过新开一个线程来检测虚引用被回收的通知。所以正确的使用soft Reference和weak Reference可以实现缓存和防止内存泄露,而虚引用一般用来实现细粒度的内存控制。比如下面代码实现一个缓存。程序在确认原来的对象要被回收之后,才申请内存创建新的缓存。Java幽灵引用的作用

八、总结

可以看出,不论是对java堆中内存空间进行分代,还是对引用进行四种类型的划分,都是为了解决在java程序运行过程中因为存在各种各样的对象,单一的垃圾回收算法没有办法高效的进行。事实上垃圾回收算法再不断的变化,每一种当前存在的垃圾回收算法都有它适应的运行环境。因此在什么时候使用什么样的垃圾回收算法,对于程序的性能来说也是至关重要的。

Java虚拟机学习记录(四)-对象的内存布局和访问定位

一、前言

jvm创建对象的过程分为类加载检查,分配对象空间,初始化类空间,设置对象信息,对象初始化。那么在分配对象空间时是如何分配的,怎么保证能够定位到对象的内存位置。

二、对象的内存布局

对象在内存中的布局可以分为三部分:对象头(Object Header),实例数据(Instance Data)和对齐填充(Padding)。

a.对象头

对象头有两部分,一部分是用于存储对象自身的运行时数据,如哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。
对象头的另外一部分是类型指针,即对象指向他的类元数据的指针(表示这个对象是哪个类实例化出来的)。并不是所有的虚拟机实现都必须在对象数据上保留类元数据的指针。因为查找对象的类元数据信息并不一定要经过对象本身(通过句柄)。另外,如果对象是一个Java数组,那在对象头中还必须要有一块用于记录数组长度的数据,因为虚拟机可以通过普通Java对象的类元数据信息确定Java对象大小,但是数据的类元数据中却无法确定数据的大小。

b.实例数据

存储对象中的各种类型的字段内容以及普通对象的指针(oops,Ordinary Object Pointers)。

c.对象填充

不是必然存在,没有特别意义,作为占位符存在。

三、对象的访问定位

建立对象是为了使用对象,对象的访问是通过栈上的reference数据来操作堆上的具体对象。reference是java虚拟机规范的一个指向对象的引用,但并没有规定如何去具体实现,一般来说,有两种实现方式:句柄和直接指针。

a.句柄

采用句柄方式会在Java堆中开辟出一块句柄池空间,Java栈中的本地变量表中存放着指向句柄池中某一个句柄的reference,然后句柄保存有指向某个实例的指针和指向方法区的对象类元数据。
reference---->句柄------>对象和类元数据,共三次指针定位
这种方式的优点是GC清理垃圾时会移动对象地址,栈中的reference不需要改变只需要改变句柄中指向对象的指针。

b.直接指针访问

reference---->对象实例数据(对象实例数据的对象头包含类元指针)------>类元数据,共两次指针定位
这种方式的优点是速度更快,节省了一次指针定位的时间。Sun HotSpot采用的就是这种对象的访问定位方式。

c.注意

在JDK1.8中,已经完全移除了方法区,类元数据的存储放在了本地内存中,这样就不会再收到方法区大小的限制。

Java虚拟机学习记录(三)-对象创建的过程

在Java程序运行时几乎每时每刻都有对象在被创建出来,从语言层面上看,只是new了一个对象,但是在虚拟机中这个对象的创建过程时比较复杂的(这里的对象仅适用于普通的Java对象,不包括数据和Class对象)。我把这其中的步骤总结为下面几步

1.类加载检查
当虚拟机接受到new指令时,首先去查常量池中能否定位到这个类的符号引用,并且检查这个符号引用的类是否已经被加载、解析和初始化过。如果没有那就必须执行类的加载过程。简单来说,就是虚拟机中有没有这个类的信息,如果没有就得去加载。

2.为对象分配内存
对象需要的内存在类加载完成之后就已经完全确定了,为对象分配内存的任务等同于在Java堆上划分出一块确定大小的内存。

这个划分内存的动作有两种情况。

如果Java堆中的内存时规整的,使用过的放一边,未使用的放另一边,中间用一个指针作为分界点的指示器。那么分配内存的动作就是将指针向未使用的那一边移动所需要的内存大小。这种分配方式叫做指针碰撞(Bump the Pointer)。

如果Java堆中的内存时不规整的,这种情况很明显不适用于指针碰撞,一般这种情况下Java虚拟机会维持一张表来记录内存的使用情况,哪些内存使用了,哪些内存时空闲的都会记录好,需要分配内存时在表中查找到合适的内存区域分配,然后更新这张表即可。这张表被称为空闲列表(Free List)

使用指针碰撞还是空闲列表由Java堆是否规整决定,而Java堆是否规整则由Java虚拟机的GC算法是否带有压缩整理功能决定。

在并发环境下,简单的修改指针指向的内存位置并不安全,因为A对象分配内存的时候,指针还没有移动的同时,B对象也要开始分配内存,因此使用的还是未发生改变的指针。解决方案有两种,一种是对分配内存空间的动作作同步处理————实际上虚拟机采用CAS配上失败重试的方式来保证操作的原子性;另一种是把内存分配动作按照线程划分在不同的空间之中进行,即每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲(Thread Local Allocation Buffer,TLAB)。哪个线程分配内存,就在那个线程的TLAB上分配,只有TLAB分配完并分配新的TLAB时,才需要同步锁定。

3.内存初始化
在分配完内存后,虚拟机需要将分配到的内存空间初始化为0(不包括对象头),如果使用TLAB,那么在TLAB分配时就可以完成这一步骤。这一步骤保证了对象中的字段不初始化就能直接使用。

package test;

public class NoInitInt {
    private int noInitInteger;

    public static void main(String[] args){
        int i = 0;
        System.out.println(i);
    }

    public int getNoInitInteger() {
        return noInitInteger;
    }

    public void setNoInitInteger(int noInitInteger) {
        NoInitInt noInitInt = new NoInitInt();
        System.out.println(noInitInt.getNoInitInteger());
    }
}

如上程序所示,输出为0

4.对象设置

Java虚拟机需要设置对象是哪个类的实例,如果才能找到类的元数据信息,对象的哈希码,对象的GC分代年龄等信息。这些信息被存放在对象的对象头中(Object Header)

5.初始化

经过上面的步骤,从Java虚拟机的角度一个新的对象已经生成了,但是从Java程序员的角度,这个对象还差一步,就是对象的初始化

Java虚拟机学习记录(二)-运行时数据区域

一、前言

Java虚拟机在执行Java程序的过程中,会把他管理的内存划分为多个不同的数据区域,这些区域各自有各自的用途,以及创建和销毁的时间,有的区域随虚拟机进程的启动而存在,有的区域依赖用户线程的启动和结束而建立和销毁。

Java虚拟机所管理的内存将会包括以下几个运行时数据区域。
1.方法区(线程共享),JDK1.7中已经开始改变,JDK1.8中被元空间替代
2.堆(线程共享)
3.虚拟机栈(线程隔离)
4.本地方法栈(线程隔离)
5.程序计数器(线程隔离)

Java虚拟机运行时涉及到的另外的内存区域
1.直接内存

二、方法区(Method Area)

这是一个线程共享的数据区域,用来保存已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。Java虚拟机将方法区描述为堆的一个逻辑部分(在HotSpot中具体表现为在方法区的内存管理和堆中的内存管理是一套方案,当然这里的一套方案并不是说他们是一模一样的),但是方法区有一个别名叫做Non-Heap(非堆),应该是为了和堆作区别(Heap)。

在HotSpot虚拟机中,方法区又被很多人叫做“永久代”(Permanent Generation),本质上两者并不等价,仅仅是HotSpot团队将GC分代收集扩展至方法区,也就是使用永久代来实现方法区。这样就和Java堆使用了同样的GC分代收集策略,不必重新实现一套管理策略。对于其他虚拟机(如BEA JRockit、IBM J9等)来说是不存在永久代的。

然而在实际应用中,使用永久代来实现方法区并不是一个好的选择,更容易遇到内存溢出的问题。在JDK1.7中,已经将字符串常量池从永久代移出了。

字符串常量池的移出:JDK1.7 被转移到Java堆中(Java Heap)

Java虚拟机规范堆方法区的限制宽松,方法区不需要连续的内存,也可以不实现垃圾收集。事实上,垃圾收集的频率在这个区域是很小的,但是并不是所有在此的数据真的是“永久”的,这个区域的垃圾回收目标主要是针对常量池的回收和对类型的卸载。这个区域的回收成绩难以令人满意。当方法区无法满足内存分配的需求时,会抛出OutOfMemoryError的错误。

关于类型卸载,我理解为在Java虚拟机运行时,会将类信息加载到方法区,当某些类不会用到的时候(unreachable),就会从方法区中卸载这个类以节省内存),具体可以看下面的文章
1.Java类加载原理解析
2.Java虚拟机类型卸载和类型更新解析

在方法区中有一块叫做运行时常量池(Runtime Constant Pool)的区域,Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分在类加载到方法区后进入运行时常量池。

字面量(literal): 我的理解为字面量指的几种基本类型。int,boolean,char,float,double,string,long,byte,null
其中float和double统称为floating-point literal,当你创建一个浮点数时,默认的是double类型。这也是为什么float a = 0.1;是错误的,你得float a = (float)0.1;
具体字面量的内容可以参考这里:Java Literals
符号引用: 符号引用是一个字符串,它给出了被引用的内容的名字并且可能会包含一些其他关于这个被引用项的信息——这些信息必须足以唯一的识别一个类、字段、方法。这样,对于其他类的符号引用必须给出类的全名。对于其他类的字段,必须给出类名、字段名以及字段描述符。对于其他类的方法的引用必须给出类名、方法名以及方法的描述符。
JVM中的直接引用和符号引用

关于常量池中的一些细节可以看这里的对比
Java常量池

方法区的变迁:
1、JDK1.2 ~ JDK6
在 JDK1.2 ~ JDK6 的实现中,HotSpot 使用永久代实现方法区;HotSpot 使用 GC 分代实现方法区带来了很大便利;

2、JDK7
由于 GC 分代技术的影响,使之许多优秀的内存调试工具无法在 Oracle HotSpot之上运行,必须单独处理;并且 Oracle 同时收购了 BEA 和 Sun 公司,同时拥有 JRockit 和 HotSpot,在将 JRockit 许多优秀特性移植到 HotSpot 时由于 GC 分代技术遇到了种种困难,所以从 JDK7 开始 Oracle HotSpot 开始移除永久代。
JDK7中符号表被移动到 Native Heap中,字符串常量和类引用被移动到 Java Heap中。

3、JDK8
在 JDK8 中,永久代已完全被元空间(Meatspace)所取代。
引用:Java 内存之方法区和运行时常量池

##三、堆(Java Heap)
和方法区一样,这也是一个线程共享的数据区域。在java虚拟机执行Java程序时,它占据了大多数的内存,几乎所有的对象的存储都是在这个区域。为什么说几乎呢?因为随着JIT编译器的发展和逃逸分析技术的逐渐成熟,栈上分配,标量替换优化技术将会导致一系列微妙的变化发生。

JIT编译器:及时编译器(Just-In-Time compiler)
逃逸分析技术:全部变量赋值,方法返回值,实例引用传递三种情况会发生指针逃逸,如果在方法内新建对象,并且这个对象没有离开过这个方法,则这个对象没有必要在堆中分配内存,直接在Java虚拟机栈中分配内存,省去了在堆中分配内存堆GC造成的压力。
什么是逃逸分析(Escape Analysis)?
栈上分配:将对象在栈上分配内存
标量替换优化技术:Java中的原始类型无法再分解,可以看作标量(scalar);指向对象的引用也是标量;而对象本身则是聚合量(aggregate),可以包含任意个数的标量。如果把一个Java对象拆散,将其成员变量恢复为分散的变量,这就叫做标量替换。拆散后的变量便可以被单独分析与优化,可以各自分别在活动记录(栈帧或寄存器)上分配空间;原本的对象就无需整体分配空间了。
HotSpot 17.0-b12的逃逸分析/标量替换的一个演示

Java堆是垃圾收集器管理的主要区域,又成”GC堆”(Garbage Collected Heap)。

从内存回收的角度来看,现在收集器基本都采用分代收集算法。所以Java堆可以细分为:新生代和老年代,再细分一点,可以分为Eden空间J,From Survivor空间,to Survivor空间

从内存分配的角度来看,Java堆可能划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer,TLAB)

Java堆的内存不要求在空间上是连续的,只要在逻辑上连续即可。

四、虚拟机栈(Java Virtual Machine Stack)

线程私有,和线程的生命周期相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时,都会创建一个栈帧(Stack Frame),用于存储局部变量表,操作数栈,动态链接,方法出口等信息。每一个方法从调用到执行完成的过程,就对应着一个栈帧从虚拟机栈入栈到出栈的过程。

局部变量表中存放了编译区可知的各种基本数据类型(boolean,byte,char,int,float,double,long,short)、对象引用(reference,它不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄和其他与此对象相关的位置)

64位的double和long会占据两个局部变量空间(slot),其余数据类型只占一个。局部变量表所需的空间在编译期间分配完成,当进入一个方法时,这个方法需要在帧中分配多少局部变量空间完全时确定的。

在Java虚拟机中,对这个区域规定了两中异常情况。1.如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError;如果虚拟机栈可以动态扩展,在扩展时无法申请到足够的内存,则会抛出OutOfMemoryError异常。

五、本地方法栈(Native Method Stack)

跟虚拟机栈类似,不过是用来存储本地方法的。

六、程序计数器(Program Counter Register)

线程私有,这是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支,循环,跳转,异常处理,线程恢复等基础功能都依赖这个。

每个线程都需要一个独立的程序计数器,保证各条线程执行中不会混乱。

如果当前执行的时Java方法,这个计数器记录的正是当前正在执行的虚拟机字节码指令的位置,如果执行的时Native方法,这个计数器为空。此区域时唯一一个在Java虚拟机规范中没规定任何OutOfMemoryError的区域。

七、直接内存(Direct Memory)

在JDK1.4中加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)和缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这个内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了频繁的在Java堆中和Native堆中复制数据。

Java虚拟机学习记录(一)-Java技术体系

##一、java技术体系概览

JDK(Java Development Kit)

  • Java程序设计语言
  • Java虚拟机
  • Java API类库

JDK是用于支持Java程序开发的最小环境。

JRE(Java Runtime Environment)

  • Java SE API子集
  • Java虚拟机

JRE是支持Java程序运行的标准环境

Java语言 Java Language
工具及工具API Java Javac JavaDoc Jar Javap Monitor JPDA
JConsole Java VisualVM JavaDB security Internationalization JMC RMI
IDL Deploy JFR Troubleshoot Scripting JVMTI Web Services
程序部署和发布 Java Web Start Applet/Java Plug-in
用户界面工具集 JavaFX
Swing Java 2D AWT Accessbility
Drag and Drop Input Methods Image I/O Print Service Sound
集成库 IDL JDBC JNDI RMI RMI-IIOP Scripting
其他基础库 Beans Int’l Support Input/Output JMX
JNI NetWorking Override Mechanism
Security Serialization Extension Mechanism XML JAXP Mechanism
语言和工具基础库 Math Collections Concurrency Utilities JAR
Logging Management Preferences API Ref Objects
Reflection Regular Expressions Versioning Zip Instrumentation
Java虚拟机 Java HotSpot Client and Server VM

这张表网上有很多版本,我以《深入理解java虚拟机》为参考,添加了JMC和JFR。可能在这划分中会有重叠部分,仅做参考。
其中

  • JDK: Java语言,工具及工具API,程序部署发布,用户界面工具集,集成库,其他基础库,语言和工具基础库,Java虚拟机
  • JRE: 程序部署发布,用户界面工具集,集成库,其他基础库,语言和工具基础库,Java虚拟机
  • Java SE API: 用户界面工具集,集成库,其他基础库,语言和工具基础库,Java虚拟机

##二. Java工具和工具API(对应了jdk中bin目录下的工具)
###自己目前使用过的有:
– java : 用来运行jar。对应bin目录下的java。
– javac: java的编译工具。对应bin目录下的javac。
– JConsole : 配置好环境的情况下,可以在控制台输入jconsole打开,jconsole可以配合JMX(其他基础库),达到对运行中的java程序进行监控甚至动态修改变量值的作用。具体的使用可以参考这一篇JMX整理,对应bin目录下的jconsole。
– security :
– keytool: 这个工具可以生成key和certificate,具体使用可以参考这一篇Java Security:keytool工具使用说明,securitykeytool 。对应bin目录下的keytool工具。
– jarsigner: jar密匙签名工具
– kinit: 主要用于获取或缓存Kerberos协议的票据授权票据。
– klist: 允许用户查看本地凭据缓存和密钥表中的条目(用于Kerberos协议)。
– ktab: Kerberos密钥表管理工具,允许用户管理存储于本地密钥表中的主要名称和服务密钥。
– policytool: 策略工具,用于管理用户策略文件(.java.policy)。

###尚未使用过的有
– javaDoc : 使用 javdoc 编译 .java 源文件时,它会读出 .java 源文件中的文档注释,并按照一定的规则与 Java 源程序一起进行编译,生成文档。对应bin目录下的javadoc。
– jar : JAR(Java Archive,Java 归档文件),是java 开发工具中的一个工具,位于JDK的安装目录的bin目录下。它是一个打包工具,有点类似winrar压缩工具,虽然一般是用来打包.class文件,但是实际上其它文件也是可以打包的。对应bin目录下的jar。
– Javap : java反编译工具。对应bin目录下的javap。
– JPDA : Java Platform Debugger Architecture(JPDA:Java平台调试架构),Java虚拟机后端和调试平台前端组成
– 1.Java虚拟机提供了Java调试的功能
– 2.调试平台通过调试交互协议向Java虚拟机请求服务以对在虚拟机中运行的程序进行调试
– jdb: Java调试工具(Java Debugger),主要用于对Java应用进行断点调试。
– Java VisualVM : java性能分析工具,对应bin目录下的jvisualvm。
– JavaDB : java的数据库。
– Int’l:可能指的是internationalization,即国际化。这里很不确定,在bin目录下有一个native2ascii,本地编码到ASCII编码的转换器(Native-to-ASCII Converter),用于”任意受支持的字符编码”和与之对应的”ASCII编码和(或)Unicode转义”之间的相互转换。
– JFR: Java飞行记录(好奇怪的翻译,Java Flight Recordings)。Java Flight Recordings (JFR) — Java 飞行记录器 – part 1
– JMC: Java任务控制工具(java Mission Control)。对应bin目录下的jmc
– RMI: Java远程方法调用(Remote Method Invocation,之前用过类似与thrift这种跨语言的远程调用框架,原理是socket通信)。对应bin目录下的
– java-rmi: Java远程方法调用(Java Remote Method Invocation)工具,主要用于在客户机上调用远程服务器上的对象。
– rmic: Java RMI 编译器,为使用JRMP或IIOP协议的远程对象生成stub、skeleton、和tie类,也用于生成OMG IDL。
– rmid: Java RMI 激活系统守护进程,rmid启动激活系统守护进程,允许在虚拟机中注册或激活对象。
– rmiregistry: Java 远程对象注册表,用于在当前主机的指定端口上创建并启动一个远程对象注册表。
– jstatd: jstatd(VM jstatd Daemon)工具是一个RMI服务器应用,用于监测HotSpot JVM的创建和终止,并提供一个接口,允许远程监测工具附加到运行于本地主机的JVM上。
– serialver: 序列版本命令,用于生成并返回serialVersionUID。
– IDL: 与RMI类似,是面向对象的远程调用,但不同的是他是跨语言的。对应bin目录下的
– idlj: IDL转Java编译器(IDL-to-Java Compiler),用于为指定的IDL文件生成Java绑定。IDL意即接口定义语言(Interface Definition Language)。
– servertool: Java IDL 服务器工具,用于注册、取消注册、启动和终止持久化的服务器。
– tnameserv: Java IDL瞬时命名服务。
– orbd: 对象请求代理守护进程(Object Request Broker Daemon),它使客户端能够透明地定位和调用位于CORBA环境的服务器上的持久对象。
– deploy:
– javafxpackager: JavaFX包装器,用于执行与封装或签名JavaFX应用有关的任务。
– pack200: JAR文件打包压缩工具,它可以利用Java类特有的结构,对普通JAR文件进行高效压缩,以便于能够更快地进行网络传输。
– unpack200: JAR文件解压工具,将一个由pack200打包的文件解压提取为JAR文件。
– Monitor: 我这里理解为一系列的java运行监视工具
– JPS:JVM进程状态工具(JVM Process Status Tool),用于显示目标系统上的HotSpot JVM的Java进程信息。对应bin目录下的jps。
– JSTAT: JVM统计监测工具(JVM Statistics Monitoring Tool),主要用于监测并显示JVM的性能统计信息。对应bin目录下的jstat。
– Troubleshoot: java的一系列错误定位工具
– JINFO: Java配置信息工具(Java Configuration Information),用于打印指定Java进程、核心文件或远程调试服务器的配置信息。对应bin目录下的jinfo。
– JStack: Java堆栈跟踪工具,主要用于打印指定Java进程、核心文件或远程调试服务器的Java线程的堆栈跟踪信息。
– JMAP: ava内存映射工具(Java Memory Map),主要用于打印指定Java进程、核心文件或远程调试服务器的共享对象内存映射或堆内存细节。。对应bin目录下的jmap。
– jhat: Heap Dump Browser, 根据dump文件进行分析,可以在浏览器中查看。
– jsadebugd: Java可用性代理调试守护进程(Java Serviceability Agent Debug Daemon),主要用于附加到指定的Java进程、核心文件,或充当一个调试服务器。
– Scripting:
– jrunscript: Java命令行脚本外壳工具(command line script shell),主要用于解释执行javascript、groovy、ruby等脚本语言。
– JVMTI: Java 虚拟机工具接口(Java Virtual Machine Toolkit Interface),用于替代在先前的 JDK 版本中作为试验功能存在的 Java 虚拟机剖析接口(Java Virtual Machine Profiling Interface,JVMPI)和 Java 虚拟机调试接口(Java Virtual Machine Debugging Interface,JVMDI)。通过 JVMTI 接口可以创建代理程序(Agent)以监视和控制 Java 应用程序,包括剖析、调试、监控、分析线程等等。
– Web Services:
– wsgen: 当从 Java 代码启动时,wsgen 命令行工具将生成 Java API for XML Web Services (JAX-WS) 应用程序所必需的工件。
– wsimport: wsimport 命令行工具用于处理现有 Web Service 描述语言 (WSDL) 文件,并生成开发 Java API for XML-Based Web Services (JAX-WS) Web Service 应用程序所必需的工件。
– schemagen: XML schema生成器,用于生成XML schema文件。
– xjc: 主要用于根据XML schema文件生成对应的Java类。

##程序部署和发布
– Java Web Start:允许用户直接从网络上运行基于java技术的应用。共有三种运行方式,无论哪一种,都会连上网络运行
– 1.从网页上点击链接。
– 2.从桌面图标或者开始菜单。
– 3.从java缓存视图中。
– Applet/Java Plug-in)
applet小程序
##用户界面工具集
– JavaFx: 2007年首次推出,具体资料可以看这些。JavaFX对Java开发者到底意味着什么JavaFX: Getting Started with JavaFX
– Swing: 是所谓的Lightweight组件,不是通过native方法来实现的,所以Swing的窗口风格更多样化。但是,Swing里面也有heaveyweight组件。比如JWindow,Dialog,JFrame。Swing由纯Java写成,可移植性好,外观在不同平台上相同。所以Swing部件称为轻量级组件, Swing是由纯JAVA CODE所写的,因此SWING解决了JAVA因窗口类而无法跨平台的问题,使窗口功能也具有跨平台与延展性的特性,而且SWING不需占有太多系统资源,因此称为轻量级组件
– Java 2D:包括Graphics,Graphics2D接口。Trail: 2D Graphics
– AWT:调用系统的native接口绘制,所以风格和系统相关。由于不同 操作系统 的图形库所提供的功能是不一样的,在一个平台上存在的功能在另外一个平台上则可能不存在。为了实现Java语言所宣称的”一次编译,到处运行”的概念,AWT 不得不通过牺牲功能来实现其平台无关性,也就是说,AWT 所提供的图形功能是各种通用型操作系统所提供的图形功能的交集。由于AWT 是依靠本地方法来实现其功能的,我们通常把AWT控件称为重量级控件。
– Accessbility: 无障碍使用,针对残疾人士。Java Accessibility Guide
– Drag and Drop: 实现拖放功能。Lesson: Drag and Drop and Data Transfer
– Input Methods:用户输入
– Image I/O:图片的输入输出
– Print Service:打印服务
– Sound:声音

##三. 集成库
– IDL: Java IDL技术添加CORBA(Common Object Request Broker Architecture)到Java平台,提供了标准的互操性和连通性。Java IDL使得分布式,web的java应用能够使用OMG(Object Management Group)定义的行业标准的IDL(Interface Definition Language)语言和IIOP(Internet Inter-ORB Protocol) 协议透明的调用远程服务。
– JDBC: Java数据库连接API(Java Database Connectivity API)。
– JNDI: Java 命名与目录接口(Java Naming and Directory Interface),所有与系统外部的资源的引用,都可以通过JNDI定义和引用。通俗点理解我觉得应该就是将配置用xml保存。JNDI 是什么
– RMI: 远程方法调用API(Remote Method Invocation)。
– RMI-IIOP:通过IIOP(Internet Inter-ORB Protocol)协议的远程方法调用(RMI)。
– Scripting:能让java应用运行js引擎。

##四. 其他基础库
– Beans: Java Beans是Java中一种特殊的类,可以将多个对象封装到一个对象(bean)中。特点是
– 可序列化
– 提供无参构造器
– 提供getter方法和setter方法访问对象的属性。

名称中的“Bean”是用于Java的可重用软件组件的惯用叫法。

    public class PersonBean implements java.io.Serializable {

    /**
     * name 属性(注意大小写)
     */
    private String name = null;

    private boolean deceased = false;

    /** 无参构造器(没有参数) */
    public PersonBean() {
    }

    /**
     * name 属性的Getter方法
     */
    public String getName() {
        return name;
    }

    /**
     * name 属性的Setter方法
     * @param value
     */
    public void setName(final String value) {
        name = value;
    }

    /**
     * deceased 属性的Getter方法
     * 布尔型属性的Getter方法的不同形式(这里使用了is而非get)
     */
    public boolean isDeceased() {
        return deceased;
    }

    /**
     * deceased 属性的Setter方法
     * @param value
     */
    public void setDeceased(final boolean value) {
        deceased = value;
    }
}
  • Internationalization Support: 国际化支持。特点如下:
    • 额外的本土化数据和任何地方运行结果相同
    • 文字信息存储在代码之外,动态加载(方便程序的翻译)
    • 不需要重新编译就能支持新的语言
    • 基于文化的数据,比如日期,货币,呈现方式
    • 可以快速本土化
  • Input/Output: 输入/输出
    • 通过数据流、序列化、文件系统的输入输出
    • 字符集、解码、编码、byte到unicode直接的转换
    • 文件、文件属性、文件系统
    • 提供建立使用异步的或可复用的、无阻塞的输入输出的服务的API
    • java.io (description) – Supports system input and output, and object serialization. to the file system
    • java.nio (description) – Defines buffers for bulk memory operations. Buffers may be allocated in direct memory for high performance.
    • java.nio.channels (description) – Defines channels, an abstraction for devices capable of performing I/O operations; defines selectors for multiplexed, non-blocking I/O
    • java.nio.channels.spi (description) – Provides implementations for channels
    • java.nio.file – Defines interfaces and classes to access files and file systems.
    • java.nio.file.attribute – Defines interfaces and classes for accessing file system attributes.
    • java.nio.file.spi – Defines classes for creating a file system implementation.
    • java.nio.charset (description) – Defines charsets, decoders, and encoders, for translating between bytes and Unicode characters
    • java.nio.charset.spi (description) – Provides implementations for charsets
  • JMX: Java管理扩展(Java Management Extensions)。提供接口结合jconsole工具使用,可以实时查看java程序运行时的变量值,以及修改他们。
  • JNI: 提供java对c的支持,可以调用c的方法。
  • NetWorking: 提供使用URLS,URIS的类,socket类提供连接服务,安全功能。
  • Override Mechanism: 重载机制。
  • Security: 提供安全相关功能的API,例如可配置的访问控制,电子签名,认证和签名,加密,安全网络交互。
  • Serialization: 序列化,可以将对象(Object)序列化变成二进制流,然后从二进制流中读取出对象。
  • Extension Mechanism: 扩展机制。在我的理解中,是指java的包机制,类加载机制,包括从URL中获取。
    • java.lang.ClassLoader
    • java.lang.Package
    • java.lang.Thread
    • java.net.JarURLConnection
    • java.net.URLClassLoader
    • java.security.SecureClassLoader
  • XML JAXP Mechanism:提供丰富的API集用来处理XML文档和数据

##五. 语言和工具基础库
– Math: 提供和数学计算相关的方法
– Collections:
– 1.分为两类
– 最基本的一类是 java.util.Collection,有以下的后代
– java.util.Set
– java.util.SortedSet
– java.util.NavigableSet
– java.util.Queue
– java.util.concurrent.BlockingQueue
– java.util.concurrent.TransferQueue
– java.util.Deque
– java.util.concurrent.BlockingDeque
– 另外的一类是 java.util.Map,Map并不是真正的Collections,但是他们有着和Collections类似的接口,所以可以像Collections一样操作。
– java.util.SortedMap
– java.util.NavigableMap
– java.util.concurrent.ConcurrentMap
– java.util.concurrent.ConcurrentNavigableMap
– 2.许多Collections的修改操作都是可选的,当尝试使用没有实现的修改操作时会抛出UnsupportedOperationException,接口的实现必须明确哪些方法是支持的。
– 如果Collections不可修改,则是unmodifiable,如果可以修改,则是modifiable
– 如果Collections一成不变,则是immutable,如果是变化的,则是mutable
– 如果Lists的大小(Size)是不变的,则是fixed-size,否则则是variable-size.
– 如果Lists支持快速的索引访问(时间为常量),则是Random Access,不支持则是sequential access。RandomAccess标记接口意味着Lists支持Random Access。
– 3.有一些元素的存储是有限制的(比如Map的key–value)
– 是一个特定的类型
– 不能为null
– obey some arbitrary predicate
– 4.接口表格

    |Interface|HashTable|Resizable Array|Balanced Tree|Linked List|Hash Table + Linked List|
    |:----:|
    |Set      |HashSet  |               |TreeSet      |           |LinkedHashSet           |
    |List     |         | ArrayList     |             |LinkedList |                        |
    |Deque    |         | ArrayDeque    |             |LinkedList |                        |
    |Map      |HashMap  |               |TreeMap      |           |LinkedHashMap           |

- 5.AbstractCollection, AbstractSet, AbstractList, AbstractSequentialList and AbstractMap类已经提供了基本功能的实现
- 6.Concurrent Collection(提供多线程下的Collections)
    - 接口
        - BlockingQueue
        - TransferQueue
        - BlockingDeque
        - ConcurrentMap
        - ConcurrentNavigableMap
    类
        - LinkedBlockingQueue
        - ArrayBlockingQueue
        - PriorityBlockingQueue
        - DelayQueue
        - SynchronousQueue
        - LinkedBlockingDeque
        - LinkedTransferQueue
        - CopyOnWriteArrayList
        - CopyOnWriteArraySet
        - ConcurrentSkipListSet
        - ConcurrentHashMap
        - ConcurrentSkipListMap
- 7.额外注意事项
   -  HashTable在官方的Collections中并没有任何提及,通过查看HashTable的源代码,发现它是继承自Dictionary,实现了Map的接口。而HashMap则是Map的实现,继承的AbstractMap也是Map的实现
    - HashTable和HashMap有以下的区别:
        - HashMap => 不同步、可空键值、效率高、containsKey/containsValue
        - Hashtable => 同步、非空键值、效率略低、contains/containsKey/containsValue

8.这一部分的内容越整理越多,还是另开一篇去记录。

– Concurrency Utilities: 提供了一系列支持并发的接口和类
– java.util.concurrent
– java.util.concurrent.atomic:一系列的原子性类,比如AtomicInteger这样的,不需要额外的同步操作就可以支持并线程并发
– java.util.concurrent.locks:提供了多种锁机制

  • JAR: 提供格式化读写JAR文件的类
  • Logging: 提供日志功能
  • Management: 提供一系列标准的接口(JMX)用来管理资源,例如应用、设备、服务、Java虚拟机。关于JMX的内容上面以及整理过了。
  • Preferences API: 提供存储,读取用户和系统配置的方法。
  • Ref(Reference Objects): 提供了和垃圾收集器相关的接口,程序可以使用引用对象来获取一个对象的引用,这样做在之后的垃圾回收中仍然可能会将这个引用对象回收。程序也可以被设计成当垃圾收集器认为一个给定的对象的可达性(gc(垃圾回收)的可达性算法,这个在后续的垃圾回收算法中会整理出来)改变时被通知。因此,引用对象在设计简单的缓存时是很有用的,因为在低内存的情况下它会被自动回收,内存充裕时,因为对象保持了引用,所以不会被垃圾回收器回收。
  • Objects: 万物皆对象,你有吗。
  • Reflection: Java反射,也是很有用的一个库。spring的IOC就是Java反射的使用 。
  • Regular Expressions:正则表达式,也是很常用的库。
  • Versioning:可以让java程序在运行时被识别它的需求的运行环境的版本号。
  • Zip: 指的应该是Java Archive (JAR) Files,可以将多个文件压缩成一个的技术。
  • Instrumentation: 和Sound相关

##六. Java虚拟机
– HotSpot Client and Server VM: 现在我们最多接触到的应该就是HotSpot虚拟机了,但是在java最初发布到现在,出现过许多或经典或优秀或有特色的虚拟机实现。
– 1.Sun Classic/Exact VM
– 2.Sun HotSpot VM
– 3.Sun Mobile-Embedded VM/Meta-Circular VM
– 4.BEA JRockit/IBM J9 VM
– 5.Azul VM/BEA Liquid VM
– 6.Apache Harmony/Google Android Dalvik VM
– 7.Microsoft JVM及其他
##七.参考文章
1. JDK自带工具一览表
2. java监控工具(jps,jstat,jstack,jmap,jvisualvm等)
3. JDK Tools and Utilities
4. Java Web Start Technology
5. Java™ Platform Overview
6. Java™ Naming and Directory Interface (JNDI)
7. Java™ Internationalization Support
8. Java™ I/O, NIO, and NIO.2
9. The Extension Mechanism
10. Collections Framework Overview
11. Java Concurrency Utilities
12. 《深入理解Java虚拟机第一章》