jvm基础(1)

1.整型数和浮点型数的表示

  原码:第一位为符号位(0为正数,1为负数)。

  反码:符号位不动,源码取反。

  正数补码:和原码相同。

  负数补码:符号位不动,反码加1。

  例如5的二进制表示可以是00000101,-6的表示:原码10000110,反码11111001,补码11111010,-1的表示:10000001,反码11111110,补码11111111

  补码的好处:0的表示唯一,数的符号位可以同数值部分作为一个整体参与运算,可以直接做加法,减法运算可以用加法来实现,即用求和来代替求差。

  例如:0的表示:正数00000000,负数10000000。

     -6+5                -4+5

       11111010         11111100

            +00000101       +00000101

           = 11111111      = 00000001

  打印正数的二进制表示

int a=-6;
for(int i=0;i<32;i++){
	int t=(a & 0x80000000>>>i)>>>(31-i);
	System.out.print(t);
}

  IEEE754浮点数的表示格式:s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm

                               符号位(1)  指数位(8) 尾数(23)

          e全0 尾数附加位全0,否则尾数附加位为1

          s*m*2(e-127)

  例如:1 10000001 0100000000000000000000

  -1*2(129-127)*(20+2-2) = -5

  将单精度浮点数转成IEEE754二进制表示的代码示例:

package work.jvm;
public class ftob {
    public static final int BITS = 32;
    public static void main(String[] args) { 
         int i;
         ftob ftb = new ftob();
         float f = -9f;
         System.out.println("float is:" + f);
         int[] b = ftb.printBinaryFloat(f);
         System.out.print("Binary is:");
         for(i = 0; i < BITS; i++){
             System.out.print(b[i]);
         }
    }
    public int[] printBinaryFloat(float f){
       int j;
       int r[] = new int[32];
       int temp = Float.floatToIntBits(f);
       for(j = BITS-1; j >= 0; --j){
           r[BITS-1-j] = ((temp>>j)&0x01);
       }
       return r;
    }
}

2.jvm运行机制

  JVM启动过程:

  

  JVM基本结构:

  

  (1)PC寄存器:每个线程拥有一个PC寄存器,在线程创建时创建,指向下一条指令的地址,执行本地方法时,PC的值为undefined。

  (2)方法区:保存装载的类信息,如i类型的常量池、字段和方法信息、方法字节码,通常和永久区(Perm)关联在一起。

  (3)java堆:和程序开发密切相关,应用系统对象都保存在Java堆中,所有线程共享Java堆,对分代GC来说,堆也是分代的,GC的主要工作区间。

  

  (4)java栈:线程私有,栈由一系列帧组成(因此Java栈也叫做帧栈),帧保存一个方法的局部变量、操作数栈、常量池指针,每一次方法调用创建一个帧,并压栈。

    栈上分配:小对象(一般几十个bytes),在没有逃逸的情况下,可以直接分配在栈上,直接分配在栈上,可以自动回收,减轻GC压力,大对象或者逃逸对象无法栈上分配。

  (5)堆、栈、方法区交互:

public class AppMain
//运行时, jvm 把appmain的信息都放入方法区
{
    public static void main(String[] args)
    //main 方法本身放入方法区。
    {
        Sample test1 = new Sample("测试1");
        //test1是引用,所以放到栈区里, Sample是自定义对象应该放到堆里面
        Sample test2 = new  Sample("测试2");
        test1.printName(); test2.printName();
    }
}
public class Sample
//运行时, jvm 把appmain的信息都放入方法区
{
    private  name;
    //new Sample实例后,name引用放入栈区里,name对象放入堆里
    public Sample(String name)
    {
        this.name = name;
    }
    //print方法本身放入 方法区里。
    public void printName()
    {
        System.out.println(name);
    }
}
 
3.jvm配置参数
  (1)Trace跟踪参数 
  -verbose:gc
  -XX:+printGC 
可以打印GC的简要信息
  -XX:+PrintGCDetails 打印GC详细信息
  -XX:+PrintGCTimeStamps 打印CG发生的时间戳
  -Xloggc:log/gc.log 指定GC log的位置,以文件输出
  -XX:+TraceClassLoading 监控类的加载
  -XX:+PrintClassHistogram 按下Ctrl+Break后,打印信息 分别显示:序号、实例数量、总大小、类型
  (2)堆分配参数
  -Xmx –Xms 指定最大堆和最小堆,例如:-Xmx20m -Xms5m
  -Xmn 设置新生代大小
  -XX:NewRatio 新生代(eden+2*s)和老年代(不包含永久区)的比值,比如4 表示新生代:老年代=1:4,即年轻代占堆的1/5。
  -XX:SurvivorRatio 设置两个Survivor区和eden的比,比如8表示 两个Survivor :eden=2:8,即一个Survivor占年轻代的1/10。
  -XX:+HeapDumpOnOutOfMemoryError,OOM时导出堆到文件。
  -XX:+HeapDumpPath 导出OOM的路径。
  -XX:OnOutOfMemoryError 在OOM时,执行一个脚本。例如:
    -XX:OnOutOfMemoryError=D:/tools/jdk1.7_40/bin/printstack.bat %p,其中printstack.bat里脚本为D:/tools/jdk1.7_40/bin/jstack -F %1 > D:/a.txt。
    当程序OOM时,在D:/a.txt中将会生成线程的dump
    也可以在OOM时,发送邮件,甚至是重启程序
  (3)栈分配参数
  -Xss 通常只有几百K,决定了函数调用的深度,每个线程都有独立的栈空间,局部变量、参数 分配在栈上。
  (4)永久区
  -XX:PermSize  -XX:MaxPermSize
  (5)堆分配参数建议
    根据实际事情调整新生代和幸存代的大小
    官方推荐新生代占堆的3/8
    幸存代占新生代的1/10
    在OOM时,记得Dump出堆,确保可以排查现场问题
4.GC算法
  (1)引用计数算法
  引用计数器的实现很简单,对于一个对象A,只要有任何一个对象引用了A,则A的引用计数器就加1,当引用失效时,引用计数器就减1。只要对象A的应用计数器的值为0,则对象A就不可能再被使用,就可以清除了。像python和actionscript用的就是引用计数算法。引用计数有两个问题:i引用和去引用伴随加法和减法,影响性能;ii很难处理循环引用。这个算法java并没有采用。
  (2)标记清除
  标记-清除算法是现代垃圾回收算法的思想基础。标记-清除算法将垃圾回收分为两个阶段:标记阶段和清除阶段。一种可行的实现是,在标记阶段,首先通过根节点,标记所有从根节点开始的可达对象。因此,未被标记的对象就是未被引用的垃圾对象。然后,在清除阶段,清除所有未被标记的对象。
  (3)标记压缩
  标记-压缩算法适合用于存活对象较多的场合,如老年代。它在标记-清除算法的基础上做了一些优化。和标记-清除算法一样,标记-压缩算法也首先需要从跟节点开始,对所有可达对象做一次标记。但之后,它并不简单的清理未标记的对象,而是将所有的存活对象压缩到内存的一端。之后,清理边界外所有的空间。
  (4)复制算法
  与标记-清除算法相比,复制算法是一种相对高效的回收方法。不适用于存活对象较多的场合,如老年代。将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,之后,清除正在使用的内存块中的所有对象,交换两个内存的角色,完成垃圾回收。年轻代的from和to两个区(也叫s1和s2)就是复制算法的两个区。
  gc可能引发stop the world(STW)现象。STW是Java中一种全局暂停的现象,这个时候所有Java代码停止运行,native代码可以执行,但不能和JVM交互。原因多半由GC引起,还有dump线程,死锁检查,堆dump也有可能引发STW。危害:i长时间(零点几秒到几十秒,甚至更长)服务停止,没有响应;ii遇到HA系统,可能引起主备切换,严重危害生产环境。
5.分代思想与可触及性
  分代思想:依据对象的存活周期进行分类,短命对象归为新生代,长命对象归为老年代。
  根据不同代的特点,选取合适的收集算法:
    少量对象存活,适合复制算法。
    大量对象存活,适合标记清理或者标记压缩。
  可触及性:
    可触及的:-从根节点可以触及到这个对象。
    可复活的:-旦所有引用被释放,就是可复活状态,因为在finalize()中可能复活该对象。
    不可触及的:-在finalize()后,可能会进入不可触及状态,不可触及的对象不可能复活,可以被回收。
6.GC参数
  (1)串行收集器。最古老,最稳定,
效率高,
可能会产生较长的停顿。
 
    参数:
-XX:+UseSerialGC
      –新生代、老年代使用串行回收
      –新生代复制算法
      –老年代标记-压缩
  
  (2)并行收集器
  
  ParNew收集器
    -XX:+UseParNewGC
    新生代并行,老年代串行
    Serial收集器新生代的并行版本
    复制算法
    多线程,需要多核支持
    -XX:ParallelGCThreads 限制线程数量
  Parallel收集器
    类似ParNew,新生代复制算法,老年代 标记-压缩,更加关注吞吐量。
    -XX:+UseParallelGC  使用Parallel收集器+ 老年代串行。
    -XX:+UseParallelOldGC  使用Parallel收集器+ 并行老年代。
    -XX:MaxGCPauseMills  最大停顿时间,单位毫秒,GC尽力保证回收时间不超过设定值。
    -XX:GCTimeRatio  0-100的取值范围,垃圾收集时间占总时间的比,默认99,即最大允许1%时间做GC。
 
  (3)CMS收集器
    Concurrent Mark Sweep 并发标记清除,这个并发的意思是与用户现场一起执行。
    标记-清除算法
    与标记-压缩相比,并发阶段会降低吞吐量
    老年代收集器(新生代使用ParNew)
    -XX:+UseConcMarkSweepGC
    -XX:+ UseCMSCompactAtFullCollection Full GC后,进行一次整理,整理过程是独占的,会引起停顿时间变长。
    -XX:+CMSFullGCsBeforeCompaction,设置进行几次Full GC后,进行一次碎片整理。
    -XX:ParallelCMSThreads,设定CMS的线程数量。
    CMS运行过程比较复杂,着重实现了标记的过程,可分为
      i.初始标记。根可以直接关联到的对象,速度快。
      ii.并发标记。(和用户线程一起)。主要标记过程,标记全部对象。
      iii.重新标记。由于并发标记时,用户线程依然运行,因此在正式清理前,再做修正。
      iv.并发清除(和用户线程一起)。基于标记结果,直接清理对象
    特点:
      尽可能降低停顿,会影响系统整体吞吐量和性能。比如,在用户线程运行过程中,分一半CPU去做GC,系统性能在GC阶段,反应速度就下降一半。
      清理不彻底,因为在清理阶段,用户线程还在运行,会产生新的垃圾,无法清理。
      因为和用户线程一起运行,不能在空间快满时再清理。-XX:CMSInitiatingOccupancyFraction设置触发GC的阈值,如果不幸内存预留空间不够,就会引起concurrent mode failure。
  (4)几个实例
    以32M堆启动tomcat
  set CATALINA_OPTS=-server -Xloggc:gc.log -XX:+PrintGCDetails -Xmx32M -Xms32M -XX:+HeapDumpOnOutOfMemoryError -XX:+UseSerialGC -XX:PermSize=32M 
  以512M堆启动tomcat
  set CATALINA_OPTS=-Xmx512m -Xms64m -XX:MaxPermSize=32M  -Xloggc:gc.log -XX:+PrintGCDetails -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:ParallelGCThreads=4
  (5)最后汇总一下GC参数:
    -XX:+UseSerialGC:在新生代和老年代使用串行收集器
    -XX:SurvivorRatio:设置eden区大小和survivior区大小的比例
    -XX:NewRatio:新生代和老年代的比
    -XX:+UseParNewGC:在新生代使用并行收集器
    -XX:+UseParallelGC :新生代使用并行回收收集器
    -XX:+UseParallelOldGC:老年代使用并行回收收集器
    -XX:ParallelGCThreads:设置用于垃圾回收的线程数
    -XX:+UseConcMarkSweepGC:新生代使用并行收集器,老年代使用CMS+串行收集器
    -XX:ParallelCMSThreads:设定CMS的线程数量
    -XX:CMSInitiatingOccupancyFraction:设置CMS收集器在老年代空间被使用多少后触发
    -XX:+UseCMSCompactAtFullCollection:设置CMS收集器在完成垃圾收集后是否要进行一次内存碎片的整理
    -XX:CMSFullGCsBeforeCompaction:设定进行多少次CMS垃圾回收后,进行一次内存压缩
    -XX:+CMSClassUnloadingEnabled:允许对类元数据进行回收
    -XX:CMSInitiatingPermOccupancyFraction:当永久区占用率达到这一百分比时,启动CMS回收
    -XX:UseCMSInitiatingOccupancyOnly:表示只在到达阀值的时候,才进行CMS回收
 
这一篇文章先写到这里,下一篇继续^-^
 
  
  
 

 

posted @ 2015-02-05 17:28  山楂条子  阅读(319)  评论(0编辑  收藏  举报