JAVA虚拟机--小结

来源:互联网 发布:人工少女 建模软件 编辑:程序博客网 时间:2024/05/13 07:41
一、java内存管理:
  java运行时,把虚拟机内存分为:方法区,虚拟机栈,本地方法栈,堆,程序计数器


  程序计数器:  当前线程所执行的字节码的行号指示器,字节码解释器工作时就是通过改变这个计数器的值
       来选取下一条需要执行的字节码指令,各条线程都有独立的程序计数器,线程私有
  
  java虚拟机栈:描述的是java方法执行的内存模型,每一个方法被调用至执行完成的过程,
就对应着一个栈帧在jvm栈中从入栈到出栈的过程,栈中存放编译器已知的基本数据类型
和引用
  
  栈溢出异常:  如果线程请求的栈深度大于虚拟机所允许的深度
  内存不足异常:当虚拟机动态扩展时,无法申请到足够多的内存时 
  
  本地方法栈:  为虚拟机使用到的native方法服务
  java堆:堆是被所有线程共享的一块内存区域,在vm启动时创建,唯一目的就是存放对象实例
java堆是垃圾收集器管理的主要区域
  堆内存不足异常:堆中没有内存完成实例分配,且堆无法扩展时


  方法区:      各个线程共享的内存区域,用于存储已被虚拟机加载的类信息,常量,静态变量,
                即时编译器编译后的代码等数据,这个区域的回收目标主要是针对常量池的回收和
和对类型的卸载
  
  运行时常量池  是方法区的一部分,用于存放编译期生成的各种字面量和符号引用,这些内容将在类加载后存放
     到方法区的运行时常量池中,运行期也可能将新的常量放入池中


  直接内存:NIO的使用,使用本地函数库分配堆外内存,然后通过java内存中的DirectByteBuffer对象作为这块内存的引用进行操作
   提高性能


二、对象访问
     Object obj =  new Object();
     “Object obj” -->java栈的本地变量表中,作为一个reference类型数据出现
     “new Object()”-->java堆中,形成一块储存了Object类型所有实例数据值的结构化内存,
             另外java堆中还必须包含能找到此对象类型数据(如对象类型,父类,实现的接口,方法等)
             的地址信息,这些类型数据则储存在方法区中
    
    主流的访问方式:
句柄和指针


三,CG回收器
    判断对象已死??


    很多(如游戏脚本)采用,java不使用,问题是很难解决相互循环引用的问题
    引用计数算法:给对象中添加一个引用计数器,每当一个地方引用它时,计数器值就+1,当引用失效时,计数器值-1,
 任何时刻计数器都为0的对象就是不可能再被使用的
    
    java和C#采用根搜索算法 
    从GC Roots的对象作为起点,向下搜索,当通过引用链不可到达指定对象时,这样对象也并非死亡!!判断非死不可,至少经历两次标记过程
    执行死刑的过程:判断该应用是否有必要执行finalize()方法,然后将之放置在一个F-Queue的队列中,由一个低优先级的线程执行
    在C和C#中调用finalize()方法,对象有机会实现一次自救,java中可以忘记该方法


    垃圾收集器:
    CMS收集器(标记--清除), 尽可能地缩短垃圾收集时,用户线程的停顿时间,停顿时间短,用户交互的程序
    Parallel Scavenge收集器,达到一个可控制的吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)
    高吞吐量,最高效率利用CPU,适合后台运算


    -XX:+UserAdaptiveSizePolicy 开关参数
    参数:-Xmx设置最大堆,MaxGCPauseMillis(最大停顿时间),GCTimeRatio(吞吐量)


    GC停顿时间缩短是以牺牲吞吐量和新生代空间来换取的


    G1收集器(标记--整理)


四,垃圾回收算法
1,标记--清除
    2,复制(新生代)
3,标记--整理,向一端移动(老年代)
4,分代收集,根据对象的存活周期不同,将内存划分为几个块,根据各个年代的特点采用最适当的收集算法

5,长期存活的对象将进入老年代
6,发生MinorGC时,虚拟机会检查之前晋升到老年代的对象平均大小是否大于老年代的剩余空间大小
  ,如果平均大小过大,则会导致full GC
7,大多数情况下,对象在新生代Eden区中分配,当Eden区没有足够内存时,虚拟机发生一次Minor GC
五,JVM监控和故障处理工具


    1,jstack:java堆栈跟踪工具
    2,JConsole jdk/bin目录下 jconsole.exe

六,系统优化
对于用户交互性强,对停顿时间敏感的系统,可以给JVM分配超大堆的前提是有把握把应用程序的Full GC频率控制得
足够低,至少要低到不影响用户使用,尽量通过定时任务的方式在深夜执行Full GC


    计划使用64位JDK,考虑下面的问题
内存回收导致的长时间停顿
64位JDK性能低于32位,64位内存消耗大,这是由于指针膨胀以及数据类型对齐补白的因素导致
需要保存程序稳定,大多数对象符合“朝生夕灭”
需要保存程序足够稳定,要是产生堆溢出就无法产生堆转储快照(dump)


    计划使用32位逻辑集群时,考虑下面的问题
32位收到最高内存(4G)的限制
大量使用本地缓存,在逻辑集群中造成较大的内存浪费,应该改为集中式缓存


    内存溢出:
集群同步导致的内存溢出
堆外内存导致的内存溢出
外部命令导致系统缓慢
服务器JVM进程奔溃 


    大量的NIO操作需要用到Direct Memory,它只能等待老年代满了后Full GC,或者内存溢出异常时,System.gc();
-XX:MaxDirectMemorySize调整大小




七,java高效并发
    1,java内存模型=主内存+线程工作内存
          
  
       
    lock:由线程执行,作用于主内存的变量,把一个变量标记为线程独占的状态
    
    read(读取):作用于主内存的变量,它把一个变量的值从主内存传输到工作内存,以便以后的load命令
    load(载入):作用于工作内存的变量,他把read来的放入工作内存的变量副本中
    use(使用):作用于工作内存的变量,他把工作内存中的一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的值的字节码指令时将会执行这个操作
    assign(赋值):作用于工作内存的变量,他把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作
 
    store(存储):作用于工作内存的变量,他把工作内存中一个变量的值传到主内存中,以便随后的write操作使用
    write(写入):作用于主内存的变量,他把store操作从工作内存中得到的变量的值放入主内存的变量中
  
    unlock:由当前线程执行,作用于主内存的的变量,把一个处于锁定状态的对象释放出来,释放后的对象才可以被其它线程锁定



 八,实现线程的三种方式:
CPU-->线程调度器-->内核线程-->轻量级进程(消耗内核资源)   轻量级进程:用户线程 = 1:1
        用户线程:运行在用户态中,无需系统帮助,高效,难度大,基本不可能实现,阻塞处理,多核映射 1:N
混合线程:依靠内核调度处理,依靠用户线程实现高并发                     M:N




 九,线程安全与锁优化
1,不可变对象:如String,我们调用它的subString(),replace(),concat()方法都不影响它原来的值,只会返回一个新构造的字符串
        2,多线程的环境中使用 绝对安全对象时,仍然需要外部同步,所以这种绝对安全是不存在的!!
        3,相对安全对象:vector,hashTable
        
     实现互斥访问的方式:
Synchronized,Semaphore,Mutex(互斥量)
        Synchronized,同步块在已进入的线程完成之前,会阻塞后面的其它线程进入,而java的线程是映射到操作系统的原生线程之上,
        如果要阻塞或者唤醒一条线程,都需要操作系统来帮忙完成,这就需要从用户态转换到核心态中,花费处理时间,对于代码简单
        的同步块,状态转换消耗的时间,比用户代码执行的时间还要长,所以说Synchronized是一个重量级的操作
        所以必要时,才使用这种操作,虚拟机本身也进行一些优化,如通知系统之前,自旋等待
  
     重入锁(ReentrantLock)
等待中断:当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情
公平锁:多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁,可以通过带布尔值的构造函数实现公共锁,
默认为不公平等待
        
绑定多个条件,ReentrantLocal对象可以同时绑定多个condition对象,只需要多次调用 newCondition()方法

如果一个变量要被多个线程访问,可以使用volatile关键字声明它为“易变的”
        如果一个变量要被某个线程独享,可以使用ThreadLocal
    
    锁优化
自适应自旋锁 
0 0