仓酷云

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 753|回复: 18
打印 上一主题 下一主题

[学习教程] JAVA编程:HotSpot假造机对象探秘仓酷云

[复制链接]
只想知道 该用户已被删除
跳转到指定楼层
楼主
发表于 2015-1-18 11:32:07 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
恰恰证明了java的简单,要不怎么没有通过c/c++来搞个这种框架?请读者起首注重本篇的标题中的限制语“HotSpot假造机”,在假造机标准中明白写道:“一切在假造机标准当中没有明白形貌的完成细节,都不该成为假造机计划者发扬制造性的牵绊,计划者能够完整自立决意一切标准中未曾形貌的假造机外部细节,比方:运转时数据区的内存怎样结构、选用哪一种渣滓搜集的算法等”。因而,本篇(全部内存篇中一切的文章)的内容会触及到假造机“自立决意”的完成,我们的会商将在HotSpotVM的局限内睁开。同时,我也假定读者已了解了假造机标准中所界说的JVM大众内存模子,比方运转时数据地区、栈帧布局等基本常识,假如读者对这些内容有疑问,能够先浏览《Java假造机标准(JavaSE7Editon)》[1]第2章或《深切了解Java假造机:JVM初级特征与最好理论》[2]的第2、3章相干内容。
对象的创立

Java是一门面向对象的编程言语,Java程序运转过程当中时时刻刻都有对象被创立出来。在言语层面上,创立对象一般(破例:克隆、反序列化)仅仅是一个new关头字罢了,而在假造机中,对象(本文中会商的对象限于一般Java对象,不包含数组和Class对象等)的创立又是如何一个历程呢?
假造时机到一条new指令时,起首将往反省这个指令的参数是不是能在常量池中定位到一个类的标记援用,而且反省这个标记援用代表的类是不是已被加载、剖析和初始化过的。假如没有,那必需先实行响应的类加载历程。
在类加载经由过程后,接上去假造机将为重生对象分派内存。对象所需内存的巨细在类加载完成后即可完整断定(怎样断定鄙人一节对象内存结构时再具体解说),为对象分派空间的义务详细便同等于一块断定巨细的内存从Java堆中分别出来,怎样划呢?假定Java堆中内存是相对规整的,一切用过的内存都被放在一边,余暇的内存被放在另外一边,两头放着一个指针作为分界点的唆使器,那所分派内存就仅仅是把谁人指针向余暇空间何处移动一段与对象巨细相称的间隔,这类分派体例称为“指针碰撞”(BumpThePointer)。假如Java堆中的内存并非规整的,已被利用的内存和余暇的内存互相交织,那就没有举措复杂的举行指针碰撞了,假造机就必需保护一个列表,纪录上哪些内存块是可用的,在分派的时分从列表中找到一块充足年夜的空间分别给对象实例,并更新列表上的纪录,这类分派体例称为“余暇列表”(FreeList)。选择哪一种分派体例由Java堆是不是规整决意,而Java堆是不是规整又由所接纳的渣滓搜集器是不是带有紧缩收拾功效决意。因而在利用Serial、ParNew等带Compact历程的搜集器时,体系接纳的分派算法是指针碰撞,而利用CMS这类基于Mark-Sweep算法的搜集器时(申明一下,CMS搜集器能够经由过程UseCMSCompactAtFullCollection或CMSFullGCsBeforeCompaction来收拾内存),就一般接纳余暇列表。
除怎样分别可用空间以外,另有别的一个必要思索的成绩是对象创立在假造机中长短常频仍的举动,即便是仅仅修正一个指针所指向的地位,在并发情形下也并非线程平安的,大概呈现正在给对象A分派内存,指针还没来得及修正,对象B又同时利用了本来的指针来分派内存。办理这个成绩有两个计划,一种是对分派内存空间的举措举行同步——实践上假造机是接纳CAS配上失利重试的体例包管更新操纵的原子性;别的一种是把内存分派的举措依照线程分别在分歧的空间当中举行,即每一个线程在Java堆中事后分派一小块内存,称为当地线程分派缓冲,(TLAB,ThreadLocalAllocationBuffer),哪一个线程要分派内存,就在哪一个线程的TLAB上分派,只要TLAB用完,分派新的TLAB时才必要同步锁定。假造机是不是利用TLAB,能够经由过程-XX:+/-UseTLAB参数来设定。
内存分派完成以后,假造机必要将分派到的内存空间都初始化为零值(不包含对象头),假如利用TLAB的话,这一个事情也能够提早至TLAB分派时举行。这步操纵包管了对象的实例字段在Java代码中能够不赋初始值就间接利用,程序能会见到这些字段的数据范例所对应的零值。
接上去,假造秘密对对象举行需要的设置,比方这个对象是哪一个类的实例、怎样才干找到类的元数据信息、对象的哈希码、对象的GC分代岁数等信息。这些信息寄存在对象的对象头(ObjectHeader)当中。依据假造机以后的运转形态的分歧,如是不是启用倾向锁等,对象头会有分歧的设置体例。关于对象头的详细内容,鄙人一节再具体先容。
在下面事情都完成以后,在假造机的视角来看,一个新的对象已发生了。可是在Java程序的视角看来,对象创立才方才入手下手——<init>办法还没有实行,一切的字段都为零呢。以是一样平常来讲(由字节码中是不是跟从有invokespecial指令所决意),new指令以后会接着就是实行<init>办法,把对象依照程序员的志愿举行初始化,如许一个真正可用的对象才算完整发生出来。
上面代码是HotSpot假造机bytecodeInterpreter.cpp中的代码片断(这个注释器完成很少时机实践利用,年夜部分平台上都利用模板注释器;今世码经由过程JIT编译器实行时差别就更年夜了。不外这段代码用于懂得HotSpot的运作历程是没有甚么成绩的)。
代码清单1:HotSpot注释器代码片断
  1. //确保常量池中寄存的是已注释的类if(!constants->tag_at(index).is_unresolved_klass()){//断言确保是klassOop和instanceKlassOop(这部分下一节先容)oopentry=(klassOop)*constants->obj_at_addr(index);assert(entry->is_klass(),"Shouldberesolvedklass");klassOopk_entry=(klassOop)entry;assert(k_entry->klass_part()->oop_is_instance(),"ShouldbeinstanceKlass");instanceKlass*ik=(instanceKlass*)k_entry->klass_part();//确保对象所属范例已经由初始化阶段if(ik->is_initialized()&&ik->can_be_fastpath_allocated()){//取对象长度size_tobj_size=ik->size_helper();oopresult=NULL;//纪录是不是必要将对象一切字段置零值boolneed_zero=!ZeroTLAB;//是不是在TLAB平分配对象if(UseTLAB){result=(oop)THREAD->tlab().allocate(obj_size);}if(result==NULL){need_zero=true;//间接在eden平分配对象retry:HeapWord*compare_to=*Universe::heap()->top_addr();HeapWord*new_top=compare_to+obj_size;//cmpxchg是x86中的CAS指令,这里是一个C++办法,经由过程CAS体例分派空间,并发失利的话,转到retry中重试直至乐成分派为止if(new_top<=*Universe::heap()->end_addr()){if(Atomic::cmpxchg_ptr(new_top,Universe::heap()->top_addr(),compare_to)!=compare_to){gotoretry;}result=(oop)compare_to;}}if(result!=NULL){//假如必要,为对象初始化零值if(need_zero){HeapWord*to_zero=(HeapWord*)result+sizeof(oopDesc)/oopSize;obj_size-=sizeof(oopDesc)/oopSize;if(obj_size>0){memset(to_zero,0,obj_size*HeapWordSize);}}//依据是不是启用倾向锁,设置对象头信息if(UseBiasedLocking){result->set_mark(ik->prototype_header());}else{result->set_mark(markOopDesc::prototype());}result->set_klass_gap(0);result->set_klass(k_entry);//将对象援用进栈,持续实行下一条指令SET_STACK_OBJECT(result,0);UPDATE_PC_AND_TOS_AND_CONTINUE(3,1);}}}
复制代码
对象的内存结构

HotSpot假造机中,对象在内存中存储的结构能够分为三块地区:对象头(Header)、实例数据(InstanceData)和对齐添补(Padding)。
HotSpot假造机的对象头包含两部分信息,第一部分用于存储对象本身的运转时数据,如哈希码(HashCode)、GC分代岁数、锁形态标记、线程持有的锁、倾向线程ID、倾向工夫戳等等,这部分数据的长度在32位和64位的假造机(暂不思索开启紧缩指针的场景)平分别为32个和64个Bits,官方称它为“MarkWord”。对象必要存储的运转时数据良多,实在已超越了32、64位Bitmap布局所能纪录的限制,可是对象头信息是与对象本身界说的数据有关的分外存储本钱,思索到假造机的空间效力,MarkWord被计划成一个非流动的数据布局以便在极小的空间内存储只管多的信息,它会依据对象的形态复用本人的存储空间。比方在32位的HotSpot假造机中对象未被锁定的形态下,MarkWord的32个Bits空间中的25Bits用于存储对象哈希码(HashCode),4Bits用于存储对象分代岁数,2Bits用于存储锁标记位,1Bit流动为0,在其他形态(轻量级锁定、分量级锁定、GC标志、可倾向)下对象的存储内容以下表所示。
表1HotSpot假造机对象头MarkWord存储内容
标记位
形态
对象哈希码、对象分代岁数
01
未锁定
指向锁纪录的指针
00
轻量级锁定
指向分量级锁的指针
10
收缩(分量级锁定)
空,不必要纪录信息
11
GC标志
倾向线程ID、倾向工夫戳、对象分代岁数
01
可倾向

对象头的别的一部分是范例指针,便是对象指向它的类元数据的指针,假造机经由过程这个指针来断定这个对象是哪一个类的实例。并非一切的假造机完成都必需在对象数据上保存范例指针,换句话说查找对象的元数据信息其实不必定要经由对象自己,这点我们鄙人一节会商。别的,假如对象是一个Java数组,那在对象头中还必需有一块用于纪录数组长度的数据,由于假造机能够经由过程一般Java对象的元数据信息断定Java对象的巨细,可是从数组的元数据中没法断定数组的巨细。
以下是HotSpot假造机markOop.cpp中的代码(正文)片断,它形貌了32bits下MarkWord的存储形态:
  1. //Bit-formatofanobjectheader(mostsignificantfirst,bigendianlayoutbelow):////32bits://--------//hash:25------------>|age:4biased_lock:1lock:2(normalobject)//JavaThread*:23epoch:2age:4biased_lock:1lock:2(biasedobject)//size:32------------------------------------------>|(CMSfreeblock)//PromotedObject*:29---------->|promo_bits:3----->|(CMSpromotedobject)
复制代码
接上去实例数据部分是对象真正存储的无效信息,也既是我们在程序代码内里所界说的各类范例的字段内容,不管是从父类承继上去的,仍是在子类中界说的都必要纪录袭来。这部分的存储按次会遭到假造机分派战略参数(FieldsAllocationStyle)和字段在Java源码中界说按次的影响。HotSpot假造机默许的分派战略为longs/doubles、ints、shorts/chars、bytes/booleans、oops(OrdinaryObjectPointers),从分派战略中能够看出,不异宽度的字段老是被分派到一同。在满意这个条件前提的情形下,在父类中界说的变量会呈现在子类之前。假如CompactFields参数值为true(默许为true),那子类当中较窄的变量也大概会拔出到父类变量的清闲当中。
第三部分对齐添补并非一定存在的,也没有出格的寄义,它仅仅起着占位符的感化。因为HotSpotVM的主动内存办理体系请求对象肇端地点必需是8字节的整数倍,换句话说就是对象的巨细必需是8字节的整数倍。对象头部分恰好似8字节的倍数(1倍大概2倍),因而当对象实例数据部分没有对齐的话,就必要经由过程对齐添补来补全。
对象的会见定位

创建对象是为了利用对象,我们的Java程序必要经由过程栈上的reference数据来操纵堆上的详细对象。因为reference范例在Java假造机标准内里只划定了是一个指向对象的援用,并没有界说这个援用应当经由过程甚么种体例往定位、会见到堆中的对象的详细地位,对象会见体例也是取决于假造机完成而定的。支流的会见体例有利用句柄和间接指针两种。


  • 假如利用句柄会见的话,Java堆中将会分别出一块内存来作为句柄池,reference中存储的就是对象的句柄地点,而句柄中包括了对象实例数据与范例数据的详细各自的地点信息。如所示。

    <br>
    经由过程句柄会见对象

  • 假如利用间接指针会见的话,Java堆对象的结构中就必需思索怎样安排会见范例数据的相干信息,reference中存储的间接就是对象地点,如所示。

    <br>
    经由过程间接指针会见对象

这两种对象会见体例各有上风,利用句柄来会见的最年夜优点就是reference中存储的是不乱句柄地点,在对象被挪动(渣滓搜集时挪动对象长短常广泛的举动)时只会改动句柄中的实例数据指针,而reference自己不必要被修正。
利用间接指针来会见最年夜的优点就是速率更快,它节俭了一次指针定位的工夫开支,因为对象会见的在Java中十分频仍,因而这类开支积小成多也是一项十分可不雅的实行本钱。从上一部分解说的对象内存结构能够看出,就假造机HotSpot而言,它是利用第二种体例举行对象会见,但在全部软件开辟的局限来看,各类言语、框架中利用句柄来会见的情形也非常罕见。
参考材料

本文撰写时次要参考了以下材料:


  • http://icyfenix.iteye.com/blog/1095132
  • http://rednaxelafx.iteye.com/blog/858009
  • http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html
[1]http://jcp.org/aboutJava/communityprocess/maintenance/jsr924/index3.html
[2]http://icyfenix.iteye.com/blog/1095132

j2EE和asp比较,其实也没什么比的,原因和我上面说那些比较差不了多少,也是稳定性,安全性,J2EE比asp高,速度上比不过asp,asp也是延续着它的拖拽控件的方法,提高速度。
第二个灵魂 该用户已被删除
沙发
发表于 2015-1-22 11:46:22 | 只看该作者
是一种使网页(Web Page)由静态(Static)转变为动态(Dynamic)的语言
乐观 该用户已被删除
板凳
发表于 2015-1-23 15:13:04 | 只看该作者
是一种使网页(Web Page)由静态(Static)转变为动态(Dynamic)的语言
小妖女 该用户已被删除
地板
发表于 2015-1-31 18:09:51 | 只看该作者
Java语言支持Internet应用的开发,在基本的Java应用编程接口中有一个网络应用编程接口(java net),它提供了用于网络应用编程的类库,包括URL、URLConnection、Socket、ServerSocket等。Java的RMI(远程方法激活)机制也是开发分布式应用的重要手段。
海妖 该用户已被删除
5#
发表于 2015-2-2 13:21:45 | 只看该作者
是一种由美国SUN计算机公司(Sun Microsystems, Inc.)所研究而成的语言
再见西城 该用户已被删除
6#
发表于 2015-2-5 23:05:17 | 只看该作者
是一种使网页(Web Page)由静态(Static)转变为动态(Dynamic)的语言
谁可相欹 该用户已被删除
7#
发表于 2015-2-11 05:18:44 | 只看该作者
设计模式是高级程序员真正掌握面向对象核心思想的必修课。设计模式并不是一种具体"技术",它讲述的是思想,它不仅仅展示了接口或抽象类在实际案例中的灵活应用和智慧
因胸联盟 该用户已被删除
8#
发表于 2015-2-14 21:07:04 | 只看该作者
Java语言支持Internet应用的开发,在基本的Java应用编程接口中有一个网络应用编程接口(java net),它提供了用于网络应用编程的类库,包括URL、URLConnection、Socket、ServerSocket等。Java的RMI(远程方法激活)机制也是开发分布式应用的重要手段。
admin 该用户已被删除
9#
发表于 2015-3-4 10:30:36 | 只看该作者
是一种突破用户端机器环境和CPU
兰色精灵 该用户已被删除
10#
发表于 2015-3-11 18:14:50 | 只看该作者
另外编写和运行Java程序需要JDK(包括JRE),在sun的官方网站上有下载,thinking in java第三版用的JDK版本是1.4,现在流行的版本1.5(sun称作J2SE 5.0,汗),不过听说Bruce的TIJ第四版国外已经出来了,是专门为J2SE 5.0而写的。
简单生活 该用户已被删除
11#
发表于 2015-3-19 06:25:08 | 只看该作者
多重继承(以接口取代)等特性,增加了垃圾回收器功能用于回收不再被引用的对象所占据的内存空间,使得程序员不用再为内存管理而担忧。在 Java 1.5 版本中,Java 又引入了泛型编程(Generic Programming)、类型安全的枚举、不定长参数和自动装/拆箱等语言特性。
金色的骷髅 该用户已被删除
12#
发表于 2015-3-19 20:51:08 | 只看该作者
是一种为 Internet发展的计算机语言
小女巫 该用户已被删除
13#
发表于 2015-4-1 13:51:38 | 只看该作者
另外编写和运行Java程序需要JDK(包括JRE),在sun的官方网站上有下载,thinking in java第三版用的JDK版本是1.4,现在流行的版本1.5(sun称作J2SE 5.0,汗),不过听说Bruce的TIJ第四版国外已经出来了,是专门为J2SE 5.0而写的。
再现理想 该用户已被删除
14#
发表于 2015-4-3 21:59:16 | 只看该作者
你就该学一学Servlet了。Servlet就是服务器端小程序,他负责生成发送给客户端的HTML文件。JSP在执行时,也是先转换成Servlet再运行的。虽说JSP理论上可以完全取代Servlet,这也是SUN推出JSP的本意,可是Servlet用来控制流程跳转还是挺方便的,也令程序更清晰。接下来你应该学习一下Javabean了,可能你早就看不管JSP在HTML中嵌Java代码的混乱方式了,这种方式跟ASP又有什么区别呢?
冷月葬花魂 该用户已被删除
15#
发表于 2015-4-6 06:30:41 | 只看该作者
是一种突破用户端机器环境和CPU
小魔女 该用户已被删除
16#
发表于 2015-4-6 17:49:43 | 只看该作者
是一种突破用户端机器环境和CPU
莫相离 该用户已被删除
17#
发表于 2015-4-14 16:18:33 | 只看该作者
是一种由美国SUN计算机公司(Sun Microsystems, Inc.)所研究而成的语言
透明 该用户已被删除
18#
发表于 2015-4-21 12:39:34 | 只看该作者
设计模式是高级程序员真正掌握面向对象核心思想的必修课。设计模式并不是一种具体"技术",它讲述的是思想,它不仅仅展示了接口或抽象类在实际案例中的灵活应用和智慧
蒙在股里 该用户已被删除
19#
发表于 2015-5-6 17:13:24 | 只看该作者
另外编写和运行Java程序需要JDK(包括JRE),在sun的官方网站上有下载,thinking in java第三版用的JDK版本是1.4,现在流行的版本1.5(sun称作J2SE 5.0,汗),不过听说Bruce的TIJ第四版国外已经出来了,是专门为J2SE 5.0而写的。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|仓酷云 鄂ICP备14007578号-2

GMT+8, 2024-11-14 20:35

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表