天天看点

JVM基础知识(原创)

jvm简介

jdk内存

JVM基础知识(原创)

java 的内存模型分为

young(年轻代也叫new区)

young分为 eden 区和两个survivor 区(from和to,这两个survivor区大小严格一至),新的对象实例总是首先放在eden 区,survivor区作为eden区和tenure(终生代)的缓冲,可以向 tenure(终生代)转移活动的对象实例。

tenured(终身代,也叫old区)

tenure中存放生命周期长久的实例对象,但并不是如它的名字那样是终生的,里面的对象照样会被回收掉。

young和tenure共同组成了堆内存,也就是heap内存或heap区。

perm(永久代)

有些旧版本也叫作:new old perm 叫法不同,表达的意思却是基本相同。perm则是非堆内存的组成部分。主要存放加载的class类级对象如class本身,method,field等等。

virtual区

在jvm启动时,就已经保留了固定的内存空间给heap内存,这部分内存并不一定都会被jvm使用,但是可以确定的是这部分保留的内存不会被其他进程使用。这部分内存大小由 -xmx 参数指定。

而另一部分内存在jvm启动时就分配给jvm,作为jvm的初始heap内存使用。影响这个的参数是 -xms ,如果 -xms 指定的值比-xmx 的小,那么两者的差值就是virtual内存值。随着程序的运行,eden区,tenured区和perm区会逐渐使用保留的virtual空间。

如果没有具体指定,初始和最大堆内存将根据机器的内存计算得出。参数defaultinitialramfraction 和 defaultmaxramfraction 会影响最终的结果,如下表所示:

formuladefault

initial heap size memory / defaultinitialramfraction memory / 64

maximum heap size min(memory / defaultmaxramfraction, 1gb) min(memory / 4, 1gb)

可以看到堆内存默认值最大不会超过1g。

jvm会根据堆内存的使用情况自动决定何时扩张和缩减实际堆内存的大小,可以用vm参数 -xx:minheapfreeratio= 和 -xx:maxheapfreeratio= 使用堆内存空闲百分比来定义,一般在32位机器上的默认值如下:

parameterdefault value

minheapfreeratio 40

maxheapfreeratio 70

-xms 3670k

-xmx 64m

-xms/-xmx:

java heap并不是越大越好,对他的一般优化原则是够用的情况下,尽可能的小,因为太大的话会浪费内存,同时影响gc的效率。当空闲堆内存所占堆内存百分比低于40%,jvm就会试图扩张堆内存空间;当空闲堆内存所占堆内存百分比高于70%,jvm就会试图压缩堆内存空间。

ps:以上默认值在不同平台会有不同的值,如果是64位系统,这些值一般需要扩张30%,来容纳在64位系统下变大的对象。

gc

gc是garbage collection的缩写,即垃圾回收机制,在java中开发人员无需使用指针来管理内存,gc是jvm对内存(实际上就是对象)进行管理的方式。

第一种为单线程gc,也是默认的gc。,该gc适用于单cpu机器。

第二种为throughput gc,是多线程的gc,适用于多cpu,使用大量线程的程序。第二种gc与第一种gc相似,不同在于gc在收集young区是多线程的,但在old区和第一种一样,仍然采用单线程。-xx:+useparallelgc参数启动该gc。

第三种为concurrent low pause gc,类似于第一种,适用于多cpu,并要求缩短因gc造成程序停滞的时间。这种gc可以在old区的回收同时,运行应用程序。-xx:+useconcmarksweepgc参数启动该gc。

第四种为incremental low pause gc,适用于要求缩短因gc造成程序停滞的时间。这种gc可以在young区回收的同时,回收一部分old区对象。-xincgc参数启动该gc。

相关调整的目标:

短生命周期的对象不要进入old区

短生命周期的对象在minor gc的时候干掉

长生命周期的对象要放到old区

长生命周期的对象可以被full gc清理掉,但是full gc要调整到尽量少发生

jvm中的classloader

类加载器(class loader)用来加载java类到java虚拟机中。一般来说,java虚拟机使用java类的方式如下:java源程序(.java文件)在经过java编译器编译之后就被转换成java字节代码(.class文件)。类加载器负责读取java字节代码,并转换成java.lang.class类的一个实例。每个这样的实例用来表示一个java类。

java程序是由许多独立的类文件组成的,每个文件对应于一个java类。此外,这些文件并非立即全部加载如内存,而是根据程序需要装入内存。classloader便是jvm中将类装入内存的零件,用户可以定制自己的classloader。jvm中缺省的classloader可以完成将本地已文件系统装入类文件的任务。用户指定的classloader可以拥有jvm缺省的classloader所不具有的功能。例如:用户可以使用自己创建的classloader从非本地硬盘或者从网络装入可执行内容。

java应用环境中不同的class分别由不同的classloader负责加载。

一个jvm中默认的classloader有bootstrap classloader、extension classloader、app classloader,分别各司其职:

bootstrap classloader:用来在jvm启动时加载核心类库,主要是 %jre_home/lib/ 目录下的rt.jar、resources.jar、charsets.jar和class等

extension classloader:负责加载java扩展类,主要是%jre_home/lib/ext目录下的jar和class

app classloader:负责加载当前java应用classpath变量中的所有类。

其中bootstrap classloader是jvm级别的,由c++编写;extension classloader、app classloader都是java类,都继承自urlclassloader超类。

bootstrap classloader由jvm启动,然后初始化sun.misc.launcher ,sun.misc.launcher初始化extension classloader、app classloader。

下图是classloader的加载类流程图,以加载一个类的过程类示例说明整个classloader的过程。

JVM基础知识(原创)

java 类加载器层次结构 

类加载器包含具有父类加载器和子类加载器的层次结构。父类加载器和子类加载器之间的关系类似于超类和子类之间的对象关系。引导类加载器是 java 类加载器层次结构的根。java 虚拟机 (jvm) 创建引导类加载器,它将加载 jvm 中包含的 java development kit (jdk) 内部类和 java.* 包。(例如,引导类加载器加载 java.lang.string。)

扩展类加载器是引导类加载器的子类加载器。扩展类加载器加载 jdk 的扩展目录中包含的所有 jar 文件。这是一种无需在类路径中添加条目即可扩展 jdk 的便捷方法。但扩展目录中的所有内容都必须是自包含的,且只能引用扩展目录中的类或 jdk 类。

系统类路径类加载器扩展 jdk扩展类加载器。系统类路径类加载器加载 jvm 类路径中的类。

加载类 

类加载器在加载类时使用委托模型。类加载器实现首先检查其缓存,查看是否已经加载所请求的类。由于不重复从磁盘中加载类,而是使用该类在缓存内存中的副本,所以,这种类验证可以提高性能。如果在类缓存中找不到该类,则当前类加载器会要求其父类加载器提供该类。仅当父类加载器也无法加载该类时,该类加载器才会尝试加载该类。如果某个类既存在于父类加载器中,又存在于子类加载器中,则将加载父类加载器中的类。遵循这种委托模型可以避免同时加载多份相同的类。加载多份相同的类会引发 classcastexception。

类加载器会先要求其父加载器加载类,然后再尝试自己加载该类。

参考至:《叱咤风云:weblogic企业级运维实战》戴冠平著

             http://longdick.iteye.com/blog/442213

             http://songyishan.iteye.com/blog/1135183

             http://hi.baidu.com/love200456/blog/item/9d4d70fbdc38c970024f564f.html

             http://blog.sina.com.cn/s/blog_677e5328010109a3.html

本文原创,转载请注明出处、作者

如有错误,欢迎指正

邮箱:[email protected]

作者:czmmiao  文章出处:http://czmmiao.iteye.com/blog/1614446