一般说栈指的是 虚拟机栈,或者说是虚拟机栈中的局部变量表
TLAB:本地线程分配缓冲,线程分配内存,现用TLAB分配,用完重新分配新的TLAB
可以设置是否启用TLAB
Mark Word:对象头:对象自身的运行时数据,哈希码,GC分代年龄,锁状态,持有的锁,偏向线程ID,偏向时间戳
HotSpot采用直接指针访问,栈中直接指向对象的地址,对象移动时,需要改变栈中的reference
4个步骤
1.初始标记 需要Stop the World标记一下GCRoots能够直接关联到的对象,速度很快
2.并发标记 进行GCRoots Tracing的过程,耗时较长,并发
3.重新标记 需要Stop the World为了修正并发标记期间因为用户程序继续运作
而导致标记产生变动的那一部分对象的标记记录,比初始标记阶段阶段稍长,比并发标记时间短得多
4.并发清除:耗时较长,并发
5.并发重置 :这个阶段,重置CMS收集器的数据结构,等待下一次垃圾回收。
CMS的优点:并发收集、低停顿
对CPU资源非常敏感,因为并发标记与并发清理的过程会占用CPU,
默认启动的回收线程数是(CPU数量+3)/4,当CPU 小于4时,占用了不少于%25的CPU资源。
CPU越多,占用降低,因此提出了ICMS(Incremental Concurrent Mark Sweep)增量式并发收集器
CMS无法处理浮动垃圾(Floating Garbage)因此可能出现Concurrent Mode Failure,
导致另一次Full GC的出现。
老年代使用达到一定标准,就会激活CMS,JDK1.5默认%68,1.6默认%92,
如果运行期间预留的内存无法满足程序需要,就会出现Concurrent Mode Failure,临时启用Serial OLD
由于使用标记-清除算法,产生了大量空间碎片,
如果无法找到足够大的连续空间来分配当前对象,不得不触发另一个Full GC。
G1 收集器 Garbage First。一款新的面向服务端的收集器
并行与并发:充分利用多CPU来缩短Stop The World的时间
分代收集:不需要其他收集器,可以独立管理GC堆
空间整合:整体上是标记-整理算法,局部上是复制算法
可预测的停顿:建立可预测的停顿时间模型
通过将堆空间划分成多个相等的独立区域,新生代与老年代不再是物理隔离的,他们都是一部分Region的集合。
有计划的避免Full GC,G1跟踪各个Region里面的垃圾的价值大小
(回收所获得的的空间大小与回收所需时间的比值),
在后台维护一个优先列表。每次都根据允许的收集时间,优先回收价值最大的Region。
1 初始标记 标记GCRoots 能够直接关联到的对象,并且修改TAMS
2 并发标记 从GC roots进行可达性分析
3 最终标记 修正标记期间因为用户程序继续运作而导致的标记变化
4 筛选回收 制定回收计划,进行回收,与CMS不同的是,筛选回收过程在最终标记后直接进行,需要Stop The world
由于只回收一部分Region,时间是用户可以控制的,而且停顿用户线程将大幅度提高收集效率。
动态对象年龄判定:如果在Survivor中相同连年大小的总和大于Survivor的一般,年龄大于或者等于该年龄的对象直接进入老年代
空间分配担当:如果老年代最大可用的连续空间大于新生代所有的对象的总空间,Minor Gc则可以确保是安全的的,可以设置是否允许担保失败,如果允许,则尝试进行MinorGC,否则进行FullGC。如果MinorGC存活对象过多,出现了HandlePromotionFailure,则在失败后发起FullGC。
一般允许担保失败,避免FullGC过于频繁。
Full==MajorGC
Minor GC:新生代垃圾回收,非常频繁,一般速度比较快
Major Gc:老年代垃圾回收,经常会伴随一次MinorGC,一般比MinorGC慢10倍以上
虚拟机栈中应用的对象
方法区里面的静态对象
方法区常量池的对象
本地方法栈JNI应用的对象
首先,在代码编译后,就会生成JVM(Java虚拟机)能够识别的二进制字节流文件(*.class)。而JVM把Class文件中的类描述数据从文件加载到内存,并对数据进行校验、转换解析、初始化,使这些数据最终成为可以被JVM直接使用的Java类型,这个说来简单但实际复杂的过程叫做JVM的类加载机制。
我们平常说的加载大多不是指的类加载机制,只是类加载机制中的第一步加载。
JVM主要完成三件事:
通过一个类的全限定名(包名与类名)来获取定义此类的二进制字节流(Class文件)。而获取的方式,可以通过jar包、war包、网络中获取、JSP文件生成等方式。
将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。这里只是转化了数据结构,并未合并数据。(方法区就是用来存放已被加载的类信息,常量,静态变量,编译后的代码的运行时内存区域)
在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。这个Class对象并没有规定是在Java堆内存中,它比较特殊,虽为对象,但存放在方法区中。
启动类加载器:Bootstrap ClassLoader,负责加载存放在JDK\jre\lib(JDK代表JDK的安装目录,下同)下,或被-Xbootclasspath参数指定的路径中的,并且能被虚拟机识别的类库(如rt.jar,所有的java.*开头的类均被Bootstrap ClassLoader加载)。启动类加载器是无法被Java程序直接引用的。
扩展类加载器:Extension ClassLoader,该加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载JDK\jre\lib\ext目录中,或者由java.ext.dirs系统变量指定的路径中的所有类库(如javax.*开头的类),开发者可以直接使用扩展类加载器。
应用程序类加载器:Application ClassLoader,该类加载器由sun.misc.Launcher$AppClassLoader来实现,它负责加载用户类路径(ClassPath)所指定的类,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
应用程序都是由这三种类加载器互相配合进行加载的,如果有必要,我们还可以加入自定义的类加载器。因为JVM自带的ClassLoader只是懂得从本地文件系统加载标准的java class文件,因此如果编写了自己的ClassLoader,便可以做到如下几点:
1)在执行非置信代码之前,自动验证数字签名。
2)动态地创建符合用户特定需要的定制化构建类。
3)从特定的场所取得java class,例如数据库中和网络中。
•全盘负责,当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入
•父类委托,先让父类加载器试图加载该类,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类
•缓存机制,缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区寻找该Class,只有缓存区不存在,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓存区。这就是为什么修改了Class后,必须重启JVM,程序的修改才会生效
1、命令行启动应用时候由JVM初始化加载
2、通过Class.forName()方法动态加载
3、通过ClassLoader.loadClass()方法动态加载
从JDK1.2开始,java虚拟机规范推荐开发者使用双亲委派模式(ParentsDelegation Model)进行类加载,其加载过程如下:
(1).如果一个类加载器收到了类加载请求,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器去完成。
(2).每一层的类加载器都把类加载请求委派给父类加载器,直到所有的类加载请求都应该传递给顶层的启动类加载器。
(3).如果顶层的启动类加载器无法完成加载请求,子类加载器尝试去加载,如果连最初发起类加载请求的类加载器也无法完成加载请求时,将会抛出ClassNotFoundException,而不再调用其子类加载器去进行类加载。
双亲委派模型意义:
系统类防止内存中出现多份同样的字节码
保证Java程序安全稳定运行
双亲委派 模式的类加载机制的优点是java类它的类加载器一起具备了一种带优先级的层次关系,越是基础的类,越是被上层的类加载器进行加载,保证了java程序的稳定运行。双亲委派模式的实现:
http://blog.csdn.net/p10010/article/details/50448491
两种办法
1. 使用URLClassloader来进行加载,需要指定路径,可以加载子类和方法的实现类
2. 使用manifest.mf文件来加载,将jar包填入classpath里面
JAVA_OPTS=-Dcom.sun.management.jmxremote
Java远程调试的原理是两个VM之间通过debug协议进行通信,然后以达到远程调试的目的。两者之间可以通过socket进行通信。
首先被debug程序的虚拟机在启动时要开启debug模式,启动debug监听程序。jdwp是Java Debug Wire Protocol的缩写。
java -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=8000,suspend=n zhc_application
这是jdk1.7版本之前的方法,1.7之后可以这样用:
java -agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend=n zhc_application
然后用一个debug客户端去debug远程的程序了,比如用Eclipse自带的debug客户端,填写运行被debug程序的虚拟机监听的端口号和地址,选择connect方式为attach。
参数 | 说明 | 默认值 |
---|---|---|
-Xms | 初始堆大小 | 物理内存的1/64(<1GB) 默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制. |
-Xmx | 最大堆大小 | 物理内存的1/4(<1GB) 默认(MaxHeapFreeRatio参数可以调整)空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制 |
-Xmn | 年轻代大小(1.4or lator)注意:此处的大小是(eden+ 2 survivor space).与jmap -heap中显示的New gen是不同的。 | 整个堆大小=年轻代大小 + 年老代大小 + 持久代大小.增大年轻代后,将会减小年老代大小.此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8 |
-Xss | 每个线程的堆栈大小 | JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K.更具应用的线程所需内存大小进行 调整.在相同物理内存下,减小这个值能生成更多的线程.但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右一般小的应用, 如果栈不是很深, 应该是128k够用的 大的应用建议使用256k。这个选项对性能影响比较大,需要严格的测试。(校长)和threadstacksize选项解释很类似,官方文档似乎没有解释,在论坛中有这样一句话:””-Xss is translated in a VM flag named ThreadStackSize”一般设置这个值就可以了。 |