JVM

jvm

  • 运行时数据区
    1. 程序计数器——正在执行的虚拟机字节码指令的地址(执行本地方法则为空)
    2. 虚拟机栈:局部变量表,操作数栈,动态链接,方法出口…
      • -Xss定义大小
      • 线程请求的栈深度超过最大值:StackOverflowError
      • 动态扩展无法申请内存:OutOfMemoryError
    3. 本地方法栈:类似虚拟机栈,区别是为本地方法服务
    4. Java堆:对象实例在此分配内存。是垃圾收集的主要区域(GC堆),分代收集算法:针对不同的对象采取不同的垃圾回收算法。
      • 新生代:生命很短的对象,垃圾回收频率最高
        • Eden
        • From Survivor
        • To Survivor
      • 老年代
      • 永久代
      • Java堆不需要连续内存,可以通过动态增加内存【OutOfMemoryError】
      • -Xms -Xmx设置最小值,最大值内存大小
    5. 方法区:存放已经被加载的类信息,常量,静态变量,编译器编译后的代码
      • 垃圾回收:常量,类(永久代),比较难实现
      • 不需要连续内存,OOMError
    6. 运行时常量池
      • (属于方法区)
      • 运行时产生的常量也可以放入常量池
    7. 直接内存区
      • 基于通道和缓冲的I/O方式:NIO(New input output)
        • BIO阻塞式IO:一直监听一个server socket,每次来一个请求,就分配一个线程处理,如果超过最大上限,就扔到队列等待。问题:大量的进程上下文切换。
        • NIO非阻塞:分而治之
          • 3类线程:mainReactor负责监听server socket,accept新链接, subReactor负责多路分离已连接的socket,读写网络数据, work线程处理具体业务
          • 如果还要分,就用NIO+异步。提高系统吞吐量,但是并不能让一个请求的等待时间下降,甚至可能增加。
          • 缓冲区Buffer为所有的原始类型提供缓存支持
          • 通道Channel连接一个特定的IO服务
          • 选择器Selector管理通道集合的信息和他们的就绪状态….不懂啊啊啊好混乱
          • NIO的框架:Mina,Netty
  • 垃圾收集:主要是Java堆和方法区,其他三个区域属于线程私有的,会自动回收
    • 判断一个对象是否可回收:
      • 方法一:引用计数器(引用时+1,引用失效-1,=0就回收)
        • 缺点:两个对象循环引用的时候,就回收不了
      • 方法二:通过GC Roots作为起点,不能达到的对象都回收
        • GC Roots:
          • 虚拟机栈中引用的对象
          • 方法区中的类静态属性引用的对象
          • 方法区中常量引用的对象
          • 本地方法栈中引用的对象
      • 方法三:引用类型:
      • 方法四:方法区的回收:
      • 方法五:finalize()
        • 类似析构函数,关闭外部资源,最好别用!!!!!
        • 如果想在gc中生存1次,那么就在finalize中重新建立引用。
    • 垃圾收集算法
      • 新生代使用:复制清理
        • 复制清理:将内存划分为2块,只使用1块,这块用完以后把还存活的复制到另一块,然后再清除这一块
          • 缺点:只能使用一半内存
          • 现在:1块较大80%的Eden空间和2块较小10%的Survivor空间,每次使用Eden和1块Survivor,复制到另一块Survivor,然后清理。如果另一块Survivor不够用,就借用老年代的空间。
      • 老年代使用:标记清理,标记整理
        • 标记清理:先标记出需要回收的对象,然后同意回收
          • 缺点:效率不高;产生大量不连续的内存碎片
        • 标记整理:先标记出需要回收的对象,然后让所有存活对象向一端移动,同时清理掉可回收的对象,也就是整理
          • 优点:没有内存碎片;如果存活率高,效率就比复制高
    • 什么时候回收:
      • eden区满,无法为新生代对象分配内存时,触发minor gc(频繁)
      • 老年代对象无法分配足够内存,触发full gc(慢)
        • 比较大的对象,一般3MB
        • 存活时间长的对象【年龄计数器:经历过几次minor gc的大风大浪还没有挂掉,就是几岁,默认15岁,也可以动态判定的】
      • Full gc其他条件:
        • 调用system.gc(),但不是强制Full gc
        • Minor gc时,发现老年代的内存不足
        • Jdk1.7之前的永久代空间不足,如果没有规定CMS gc,就会触发full gc,如果full gc还是解决不了,就跑出OOMError,采用方法:使用CMS gc或者增大永久代空间
        • Concurrent Mode Failure
          • CMS gc的时候,有对象要放入老年代,而此时老年代空间不足(暂时性的),就会报错,触发full gc
  • 类加载机制:类是在运行期间动态加载的
    • 生命周期
    • 初始化的时机:
      1. New实力化对象时,getstatic,putstatic获取和设置一个类的静态字段时(除了final),invokestatic调用一个类的静态方法
      2. 使用reflect对类进行反射调用的时候
      3. 初始化一个类,结果发现他的父类没有初始化的时候
      4. Jvm启动时,初始化主类(main所在的类)
      5. jdk7的动态语言支持时,如果一个methodhandle实例最后的解析结果为REF_getStatic, REF_putStatic,REF_invokeStatic的方法句柄,那么这个方法句柄的类需要经过初始化
        • 除此之外,所有引用类的方式都不会触发初始化,称为被动引用:
        • 通过子类引用父类的静态字段,不会导致子类初始化
        • 通过数组定义类,不会初始化类,而是初始化数组类
        • 常量的类
    • 类加载过程
      • 加载Loading:(是类加载的一个阶段)
        • 通过一个类的全限定名来获取定义此类的二进制字节流
        • 将这个字节流所代表的静态存储结构转化为方法区的运行时存储结构
        • 在内存中生成一个代表这个类的Class对象,作为方法区这个类的各种数据的访问入口
      • 验证Verification:验证字节流是否符合当前虚拟机的要求,并且不会危害安全
        • 文件格式验证;是否符合规范?jvm版本?
        • 元数据验证;语义符合规范?
        • 字节码验证;语义合法?符合逻辑?
        • 符号引用验证;匹配性校验。
      • 准备Preparation:为static类变量分配内存(方法区)并设置初始值(不算实例变量,实例变量是在java堆中的)
        • 如果不是final常量:初始化为0,然后再赋值
        • 如果是,直接初始化
      • 解析:将常量池的符号引用替换为直接引用?????不懂
      • 初始化:
        • 类变量的真正赋值
        • 静态语句块
        • 类加载器
          • 启动类加载器:加载jvm自身需要的类,比如java\javax\sun开头的类
          • 扩展类加载器:/lib/ext目录下的类
          • 系统类加载器:classpath路径中的类(默认)
        • 双亲委派:子类加载器收到请求,先发送给父加载器,父亲能加载就加载
Table of Contents