JVM,JAVA虚拟机,内存机制,线程.doc_第1页
JVM,JAVA虚拟机,内存机制,线程.doc_第2页
JVM,JAVA虚拟机,内存机制,线程.doc_第3页
JVM,JAVA虚拟机,内存机制,线程.doc_第4页
JVM,JAVA虚拟机,内存机制,线程.doc_第5页
已阅读5页,还剩21页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

JVM的机制学习大纲JVM标准结构Java代码执行机制源码编译机制JVM规范中定义了class文件的格式,JDK在编译java源码时,使用了javac,javac编译的步骤:1. 分析和输入到符号表(Parse And Enter)Parse做的是词法和语法的分析。词法分析:将代码字符串转变为token序列语法分析:将根据语法由token序列生成抽象语法树Enter将符号输入到符号表,通常包括确定类的超类和接口,添加默认构造器等。2. 注解处理3. 语义分析和生成class文件通常生成class文件不知包括字节码,一般包括结构信息,元数据,方法信息。下面是一个例子:Compiled from Foo.java/类/继承的超类/实现的接口的声明信息public class Foo extends java.lang.Object SourceFile: Foo.java/class文件格式版本号,major version 50 表示jdk 6, 49 为jdk 5只有高版本能执行低版本的class文件 minor version: 0 major version: 50/常量池,存放了所有的Field名称,方法名,方法签名,类型名,代码及class文件中的常量值 Constant pool:const #1 = Method #7.#27; / java/lang/Object.:()Vconst #2 = Field #6.#28; / Foo.count:Iconst #3 = class #29; / java/lang/Exceptionconst #4 = String #30; / count overflowconst #5 = Method #3.#31; / java/lang/Exception.:(Ljava/lang/String;)Vconst #6 = class #32; / Fooconst #7 = class #33; / java/lang/Objectconst #8 = Asciz MAX_COUNT;const #9 = Asciz I;const #10 = Asciz ConstantValue;const #11 = int 1000;const #12 = Asciz count;const #13 = Asciz ;const #14 = Asciz ()V;const #15 = Asciz Code;const #16 = Asciz LineNumberTable;const #17 = Asciz LocalVariableTable;const #18 = Asciz this;const #19 = Asciz LFoo;const #20 = Asciz bar;const #21 = Asciz ()I;const #22 = Asciz StackMapTable;const #23 = Asciz Exceptions;const #24 = Asciz ;const #25 = Asciz SourceFile;const #26 = Asciz Foo.java;const #27 = NameAndType #13:#14;/ :()Vconst #28 = NameAndType #12:#9;/ count:Iconst #29 = Asciz java/lang/Exception;const #30 = Asciz count overflow;const #31 = NameAndType #13:#34;/ :(Ljava/lang/String;)Vconst #32 = Asciz Foo;const #33 = Asciz java/lang/Object;const #34 = Asciz (Ljava/lang/String;)V;/将符号输入到符号表时生成的默认构造器方法public Foo(); Signature: ()V LineNumberTable: line 1: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this LFoo; Code: Stack=1, Locals=1, Args_size=1 0: aload_0 1: invokespecial #1; /Method java/lang/Object.:()V 4: return LineNumberTable: line 1: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this LFoo;/bar方法的元数据信息public int bar() throws java.lang.Exception; Signature: ()I/对应字节码的源码行号信息,可在编译的时候通过-g:none 去掉行号信息,行号信息对于查找问题而言至关重要,因此最好保留 LineNumberTable: line 5: 0 line 6: 15 line 7: 19 line 9: 29/局部变量信息,如生成的class文件中无局部变量信息,则无法知道局部变量的名称,并且局部变量信息是和方法绑定的,接口是没有方法体的,所以ASM之类的在获取接口方法时,是拿不到方法中参数的信息的 LocalVariableTable: Start Length Slot Name Signature 0 33 0 this LFoo; Code: Stack=3, Locals=1, Args_size=1/对应方法的字节码 0: getstatic #2; /Field count:I 3: iconst_1 4: iadd 5: dup 6: putstatic #2; /Field count:I 9: sipush 1000 12: if_icmplt 29 15: iconst_0 16: putstatic #2; /Field count:I 19: new #3; /class java/lang/Exception 22: dup 23: ldc #4; /String count overflow 25: invokespecial #5; /Method java/lang/Exception.:(Ljava/lang/String;)V 28: athrow 29: getstatic #2; /Field count:I 32: ireturn LineNumberTable: line 5: 0 line 6: 15 line 7: 19 line 9: 29 LocalVariableTable: Start Length Slot Name Signature 0 33 0 this LFoo;/记录有分支的情况(对应代码中的iffor,while等) StackMapTable: number_of_entries = 1 frame_type = 29 /* same */异常处理表 Exceptions: throws java.lang.Exceptionstatic ; Signature: ()V LineNumberTable: line 3: 0 Code: Stack=1, Locals=0, Args_size=0 0: iconst_0 1: putstatic #2; /Field count:I 4: return LineNumberTable: line 3: 0类加载生成的class文件,可被JVM装载,并形成Class对象的机制,称为类加载机制之后就可对Class对像进行实例化并调用,类加载机制可在运行时动态加载外部的类。JVM的类加载分为三步: 装载,链接,初始化装载和链接之后,即将二进制字节码转化成Class对象初始化则不是加载时,必须的步骤,最迟可在首次调用对象前执行,行为包括给静态变量赋值,调用()等装载:装载通过类的全限定名+ClassLoader实例ID对其进行标识链接:对二进制字节码的格式进行校验,初始化装载类中的静态变量及解析类中调用的接口,类初始化:执行类中的静态初始化代码,构造器代码及静态属性的初始化,以下情况初始化会被触发:1. 调用了new2. 反射调用了类中的方法3. 子类调用了初始化4. JVM启动过程中指定的初始化类类执行字节码解释执行在源码编译阶段将源码编译成JVM字节码后,要由JVM在运行期对其进行解释并执行,称为字节码解释执行方式对于字节码JVM有自己的执行指令,invokestatic, invokevirtual, invokeinterface, invokespecial, 分别对应static方法,对象实例方法,接口方法,private方法/编译源码后生成的方法的调用。eg:public class Demopublic void execute()A.execute();A a = new A();a.bar();IFoo b = new B();b.bar();class Apublic static int execute()return 1+2;public int bar()return 1+2;class B implements IFoopublic int bar()return 1+2;interface IFoopublic int bar();对上面的代码编译,然后javap c Demo 查看其execute方法字节码Compiled from Demo.javapublic class Demo extends java.lang.Objectpublic Demo(); Code: 0: aload_0 1: invokespecial #1; /Method java/lang/Object.:()V 4: returnpublic void execute(); Code: 0: invokestatic #2; /Method A.execute:()I 3: pop 4: new #3; /class A 7: dup 8: invokespecial #4; /Method A.:()V 11: astore_1 12: aload_1 13: invokevirtual #5; /Method A.bar:()I 16: pop 17: new #6; /class B 20: dup 21: invokespecial #7; /Method B.:()V 24: astore_2 25: aload_2 26: invokeinterface #8, 1; /InterfaceMethod IFoo.bar:()I 31: pop 32: return上面可以看到4个指令的使用情况JDK栈体系线程创建后,产生PC(程序计数器)和栈,PC:存放了下一条要执行的指令在方法内的便宜量;栈:栈中存放了栈帧(StackFrame),每个方法的每次调用都会产生栈帧栈帧主要分为局部变量去和操作数栈两部分,局部变量区用于存放方法中局部变量和参数操作数栈用于存放方法执行过程中产生的中间结果,栈帧中还有一些杂用控件,指向方法已解析的常量池的引用,其他一些VM内部实现需要的数据eg:public class Demo1public static void foo()int a=1;int b=2;int c=(a+b) * 5;编译Demo1.java查看字节码:Compiled from Demo1.javapublic class Demo1 extends java.lang.Objectpublic Demo1(); Code: 0: aload_0 1: invokespecial #1; /Method java/lang/Object.:()V 4: returnpublic static void foo(); Code: 0: iconst_1/将类型为int,值为1的常量放入操作数栈 1: istore_0/将操作数栈中栈顶的值弹出放入局部变量区 2: iconst_2/将类型为int,值为2的常量放入操作数栈 3: istore_1/将操作数栈中栈顶的值弹出放入局部变量区 4: iload_0/装载局部变量区中的第一个值到操作数栈 5: iload_1/装载局部变量区中的第二个值到操作数栈 6: iadd/执行int类型的add指令,并将结果放入操作数栈 7: iconst_5/将类型为int,值为5的常量放入操作数栈 8: imul/执行int类型的mul指令,并将结果放入操作数栈 9: istore_2/将操作数栈中栈顶的值弹出放入局部变量区 10: return/返回对于方法指令的解释执行,执行方式为经典冯。诺依曼体系中的FDX循环方式,即获取下一条指令,解码并分派,然后执行。在实现FDX循环时有switch-threading,token-threading,direct-threading,subroutine-threading,inline-threading等多种方式switch-threading,最简单:while(true)int code = fetchNextCode();switch(code)case IADD:/do addcase :/do sth/每次执行完后回到起点,重新获取下一条,并继续switch,大部分时间都花在跳转和获取下一条上token-threading : 在switch-threading上稍作改进IADD: /do add;fetchNextCode();dispatch();ICOUNT_0:push(0);fetchNextCode();dispatch();虽然冗余了fetchNextCode和dispatch,消耗内存大一些,但由于去除了switch,性能会好一些SUN JDK的重点为编译成机器码,因此采用了token-threading,让解释执行更高效,还做了其他的优化: 栈顶缓存(top-of-stack caching)和部分栈帧共享栈顶缓存:因为在很多操作中要将值放入操作数栈,导致了寄存器和内存的不断交换数据,Sun JDK采用了一个栈顶缓存,将本来放在操作数栈顶的值缓存在寄存器上,这样对于大多只要一个值的操作而言,不需要将数据放入操作数栈,可在寄存器中计算,完了再放回操作数栈部分栈帧共享:当一个方法调用另一个方法时,传入另一个方法的参数已经放在了操作数栈中,Sun JDK做了优化,当调用方法时,后一方法可将前一方法的操作数栈作为当前方法的局部变量,从而节省数据copy带来的消耗编译执行因为解释执行效率低,所以为了提升性能,Sun JDK将字节码编译成机器码,编译在运行时进行,称为JIT编译器。Sun JDK在执行过程中对执行频率高的代码进行编译,对执行不频繁的代码进行解释,在编译上Sun JDK提供了两种方式:client compiler(-client), server compiler(-server)client compiler: 又称为C1,只做少量性能开销比高的优化,占用内存少,适合于桌面交换式应用,其他方面的优化有方法内联,去虚拟化,冗余消除方法内联:是在调用方法时,可直接将调用的方法的指令直接植入到当前方法中,通过-XX:MaxInlineSize=35可以配置当前允许内联的方法的大小,=Eden space大小的一半,就在OG上分配.eg:PS GC(并行回收GC)测试,以参数java -Xms20M -Xmn10M -Xmx20M -XX:SurvivorRatio=8 -XX:+UseParallelGC PSGCDirectOldDemo运行例子,并以jstat来查看两次,看到bytes4数组分配在OG(Old Generation)上jstat -gc pid;查看结果:分析: 第一次时OU为0, OG上的占用空间为0, EC为Eden space的空间大小, 要分配bytes4时,bytes4的大小为4096=EC/2, 所以分配在了OG上,因此第二次查看时,OU=4096.0.3.并行GC(ParNew)并行GC在Eden S0,S1的大小分配上和串行GC是一样的。和PS GC的区别在于并行GC配合OG使用CMS GC,CMS GC在进行OG GC时,有些过程是并发的,如此时发生Minor GC,需要做一些处理,而PS GC没有这些处理。在Eden上分配内存不足时,JVM即触发Minor GC,也可在程序中手动System.gc调用示例Minor GC:javac MinorGCDemo.javajava -Xms40M -Xmx40M -Xmn16M -verbose:gc -XX:+PrintGCDetails这里分配整个JVM方法栈大小为40M, 其中New Generation 为16M, Eden 为 12M, 两个survivor space各2M,OG为24M通过jstat -gcutil pid 1000 10 (间隔1s,查看10次)查看Eden , S0, S1, old在minor GC时的变化结果:OG 和 PG可用的GCJDK提供了串行,并行,并发三种GC来对OG和PG占的内存进行GC。1.串行:串行基于标记-清除-压缩(Mark-Sweep-Compact)方式,在使用串行时,OG的内存分配和NG是一样的。串行分三步:a) 从根集合开始扫描对象b) 对未标记的对象进行清除c) 执行滑动压缩,将存活的对象向OG空间开始处移动,最终在OG中留出一块连续的空间到结尾处的空间。整个过程是单线程的,需要暂停应用2.并行采用了Mark-Compact的方式,内存分配上和串行相同并行GC分三步:a) 根据CPU的数量,将OG分为多个region,并发扫描这些region,进行标记b) 一般经过多次GC,OG最左边存放多是活跃的对象,从左往右扫描,找到第一个值得Compact的region,这个region左边的区域认为是dense prefix 不进行操作, 然后继续往右扫描, 标记region的源和目标,切换这些对象的指针,并在region上做标志,同时清除regions中其他不存活对象的空间,此过程是单线程的c) 根据上面的分析,找到目标region,和完全没有存活对象的region,并行进行对象移动和region回收3.并发(CMS Concurrent Mark-Sweep GC)因为Mark-Sweep扫描整个OG需要较长时间,Sun JDK提供了CMS GC的方式,使大部分动作与应用并发的进行。CMS用了Mark-Sweep的方式,在清除后,会产生很多个空白空间,它将这些空间在一个free list中标记,当NG的新对象请求OG空间,在free list中可以找到够用的空间使用。同时,因为CMS与应用并发,会导致空间的分配和回收的同时进行,导致free list竞争激烈,CMS为了避免这个现象,引入了Mutual exclusion locks, 以JVM分配内存为先。整个CMS过程中要进行三次的扫描,标记。Full GC当OG,PG触发GC时,出现对NG,OG,PG都进行GC的, 称为Full GC。Full GC被触发时,NG先按配置进行NG的GC,然后OG,PG进行GC。但其中有一种情况,当Minor GC前,NG移到OG的对象大于OG的剩余空间,则不进行Minor GC, 直接采用OG的GC方式对NG,OG,PG进行GC。除了直接调用System.gc外,触发Full GC的四种情况1OG空间不足上面说到的,转入OG的对象太大,OG不足,则触发Full GC, 如果Full GC之后还不足,则抛出 java.lang.OutOfMemoryError: Java heap space这种情况调优:不要创建过大对象和数组2. PG空间满PG存放了class信息,当系统要加载的类,反射的类和调用的方法过多时,PG满,而且没使用CMS的情况下,Full GC使用。如果还不行,则抛出java.lang.OutOfMemoryError: PermGen space3. CMS GC出现promotion failed 和 concurrent mode failurepromotion failed: Minor GC时,survivor space放不下,转入OG,但是OG不足concurrent mode failure: 在CMS GC时,同时有对象要放入OG, OG不足4. 统计得到的Minor GC晋升到OG的平均大小大于OG的剩余空间小结:client,server模式默认GCNGOG & PGClient串行GC串行GCServer并行回收GC并行GCSun JDK GC组合方式NG OG PG-XX:+UseSerialGC串行GC串行GC-XX:+UseParallelGC并行回收GC并行GC-XX:+UseConcMarkSweepGC并行GC并发GC当出现concurrent mode failure 时采用串行GC-XX:+UseParallelOldGC并行回收GC并行GC-XX:+UseConcMarkSweepGC-XX:+UseParNewGC串行GC并发GC当出现concurrent mode failure 或 promotion failed 时采用串行GC不支持的组合1.-XX:+UseParNewGC XX:+UseParallelOldGC2.-XX:+UseParNewGC XX:+UseSerialGCJVM内存状况查看方法和分析工具输出GC日志将JVM支持的日志输出到控制台或文件,到Console:JVM启动参数加入: -XX:+PrintGC XX:+PrintGCDetails XX:+PrintGCTimeStamps XX:+PrintGCApplicationStoppedTime, 按照参数的顺序分别输出GC的简要信息,GC的详细信息,GC的时间信息及GC造成的应用暂停的时间到文件:在上面的启动参数中加入: -Xloggc:gc.log可指定gc信息输出到gc.log可用于GC分析的参数还有:-verbose:gc,-XX:+PrintTenuringDistribution等。GC PortalGC Portal提供了对GC日志的图标分析,需要运行在tomcat上,还有数据库的支持,配置有点麻烦。GC Portal对于JDK6的日志,只能分析6M以下的。GC Portal提供了吞吐量分析,耗费CPU时间,造成的应用暂停时间,每秒从NG到OG的数量,minor GC的状况及Full GC的状况等Jconsole是JDK5以上版本自带工具,图形化,直接运行Jconsole.exe或Jconsole.sh,查到JAVA进程的pid,就可查看相应进程的状况。Jconsole 显示JVM的很多信息: 内存,线程,类和Mbean等。JVisualVM是JDK6 update 7之后推出的,类似Jprofiler的工具JMapJDK自带的用于分析JVM内存状况的工具查看JVM各个代的内存状况:jmap heap pid查看JVM各个代的内存占用情况: jmap histo pid导出整个JVM中的内存信息:jmap dump:format=b,file=文件名 pidJHatJDK6以上自带的用于分析JVM堆dump文件的工具,可分析JVM dump中对象的内存占用状况,引用关系等jhat-J-Xmx1024Mfile执行后,等待console显示Started HTTP sever on port 7000, 然后就可以在浏览器中,访问http:/ip:7000了JStatJDK自带的一个统计分析JVM运行状况的工具,处理可以分析GC状况外,还可用于分析编译的状况、class加载的状况等Eclipse Memory Analyzer(MAT)Eclipse提供的用于分析JVM 堆dump得插件,MAT网址:/mat/此插件分析,比较耗内存,最好先将ECLIPSE的JVM内存设大一点。JVM线程机制竞争同步机制这段代码的执行顺序:1 JVM在堆(main memory)中,给i分配

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论