Java虚拟机

JVM
  • HotSpot虚拟机
    • 对象的创建过程:
      • 1:new时先检查这个类是否已被加载、解析和初始化过。如果没有,要进行类加载过程。
      • 2:分配内存:指针碰撞或空闲列表。
      • 3:初始化为零值。
      • 4:设置对象头的信息:
        • 1:类型指针:这个对象是哪个类的实例
        • 2:运行时数据:哈希码、对象的GC分代年龄、锁状态标志、线程持有的锁……
    • 对象的内存布局:
      • 1:对象头:存储对象自身的运行时数据、类型指针(对象是哪个类的实例)。
      • 2:实例数据:我们定义的对象的内容。
      • 3:对齐填充:占位,HotSpot要求对象起始地址是0字节的整数倍。
    • 对象访问定位(从虚拟机栈的本地变量表指向对象的实例数据的过程):
      • 1:reference指向对象的句柄地址,句柄地址包含指向实例数据和类型数据的两个指针,句柄池中的句柄地址指向堆中实例池中的实例数据和方法区中的类型数据。好处是对象移动时,只用改变实例数据指针,不用改变reference。
      • 2:reference直接指向实例数据。
    • 锁的状态总共有四种:无锁状态、偏向锁、轻量级锁和重量级锁。
    • 定位OOM异常:
      • 1:堆:OutOfMemoryError:Java heap space:不断创建对象,并避免GC清除这些对象。while(true)+new对象把对象放在list集合里。-Xmx 20m -Xms 20m
      • 2:虚拟机栈和本地方法栈溢出:
        • 1:StackOverflowError:线程请求的栈深度大于虚拟机允许的最大深度:-Xss128k加上不断递归调用自身。
        • 2:OutOfMemoryError:扩展栈时无法申请到足够的空间:两个while(true),一个不断创建新线程,一个让线程一直运行不要停。-Xss2M要设置大一点。做法:在不能减少线程数或更换64位虚拟机的情况下,减少最大堆和减少栈容量来换取更多线程,将Xss设置小一点,每个线程的栈空间少了,才能容纳更多线程。
      • 3:方法区中的运行时常量池溢出:OutOfMemoryError:PermGen space:while(true)list.add(String.valueOf(i++).intern())-XX:PermSize=10M -XX:MaxPermSize=10M
      • 4:本机直接内存溢出:OutOfMemoryError:-XX:DirectMemorySize=10M
  • GC
    • 1:回收堆
    • 对象死亡过程:标记不可达的对象,判断是否重写了finalize()方法,重写了就执行finalize()方法,如果这里没有将自己赋值给某个变量,则回收该对象不再执行finalize()方法
    • 对象是否死亡的判断:
      • 1:引用计数法:引用+1,引用失效-1
      • 2:可达性分析算法:GC Roots作为起点,能到达的对象就是可达的,不能到达的就是要回收的。
    • GC Roots:
      • 1:虚拟机栈中局部变量表引用的对象
      • 2:类静态属性引用的对象
      • 3:常量引用的对象
      • 4:本地方法栈JNI(Native方法)引用的对象
    • 引用强度:
      • 1:强引用:new:永远不回收
      • 2:软引用:SoftReference类实现:要发生内存溢出之前,列进回收范围,否则不回收。如果这测回收还没有足够内存,则抛出内存溢出异常
      • 3:弱引用:WeakReference类实现:每次垃圾收集都会回收它
      • 4:虚引用:PhantomReference类实现:无法获得一个对象实例,每次垃圾收集都会回收它,回收时会收到系统通知
    • 2:回收方法区
      • 1:回收常量:有没有String对象引用了这个常量。
      • 2:回收类:该类所有实例已回收、加载该类的ClassLoader已回收、该类对应的java.lang.Class对象没有在任何地方被引用,没有用反射。
    • 垃圾收集算法:
      • 1:标记-清除法
      • 2:复制法:回收新生代,叫Minor GC,内存分为一块大的、两块小的,8Eden,1Survivor,1Survivor,如果小的块不能存放,就进入老年代。
      • 3:标记-整理法:回收老年代
      • 4:分代收集:老年代新生代用不同的算法
    • stopthe world:GC Roots开始查找引用链要停顿。在OopMap的协助下,HotSpot剋快速准确地完成GC Roots枚举
    • 安全点
    • 内存分配普遍规则:
      • 1:对象优先在新生代Eden区中分配。
      • 2:大对象直接进入老年代,需要大量连续内存空间的对象,长字符串、数组。虚拟机有个参数能控制直接进入老年代对象的大小。
      • 3:长期存活的对象进入老年代。在Eden出生,经过Minor GC后存活,能被Survivor容纳,年龄加一。也有参数设置
      • 4:动态对象年龄判定:不一定要等到参数设置的年龄,当某个年龄的对象个数>Survivor空间/2,大于等于该年龄进入老年代
      • 5:空间分配担保:对于复制法。老年代最大可用的连续空间是否大于新生代所有对象总空间。如果不满足,则看设置值是否允许担保失败。如果允许,则判断老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小。
  • 类的生命周期
    加载、验证、准备、解析(不定)、初始化、使用(不定)、卸载
    • 加载阶段:
      • 1:获取类的二进制字节流(.class文件),类加载器完成,不是虚拟机。
      • 2:将字节流???
      • 3:在内存中生成java.lang.Class对象,作为方法区这个类的各种数据的访问入口
    • 验证阶段:确保Class文件的字节流包含的信息符合虚拟机的要求,安全。当Class文件不是用java源码编译来的可能有危险。
      • 1:文件格式验证:是否符合Class文件格式的规范,能被当前版本虚拟机处理
      • 2:元数据验证:符合java语言规范
      • 3:字节码验证:语义合法合逻辑,不会危害虚拟机
      • 4:符号引用验证:符号是不是都写对了,发生在字符引用转化为直接引用的时候。
    • 准备阶段:为静态变量分配内存、设置零为初值,分配到方法区,但如果是final修饰的变量,则该变量被初始化为指定的值。不操作实例变量。
    • 解析阶段:将常量池内的符号引用替换为直接引用的过程。
      • 类或接口的解析:将引用符号N解析为一个类或接口C的直接引用
      • 字段的解析
      • 类方法解析
      • 接口方法解析
    • 初始化:是执行类构造器<clinit>(),它合并了static{}块和类变量的赋值操作
      • 类立即初始化有且只有:
        • 1:new、类的静态字段、调用类的静态方法
        • 2:反射调用
        • 3:初始化一个类的时候,它的父类
        • 4:main()方法那个类
        • 5:看不懂
  • 双亲委派模型:一个类加载器收到了类加载请求,它不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一层的加载器都是如此,因此所有加载请求都会传到最高的启动类加载器。只有当父类反馈自己无法加载时,子加载器才会尝试自己去加载。
  • ClassLoader类加载器用来把类装载进内存,jvm运行时会产生3个类加载器组成初始化加载器:辈分由高到低
    • 启动类加载器(无法获取,C++实现,而且是虚拟机来控制,其它都是Java实现,不是虚拟机控制):负责把<HAVA_HOME>\lib目录中或者-Xbootclasspath下的类库加载到虚拟机内存中。
    • 扩展类加载器ExtClassLoader:负责加载<HAVA_HOME>\lib\ext下或者java.ext.dirs系统变量指定路径下所有类库。可以直接使用。
    • 应用程序类加载器(自定义)getSystemClassLoader():负责加载classpath上指定的类库。如果没有自定义,这就是默认的类加载器。
  • 多线程与同步
    • 服务器性能好坏:每秒事务处理数TPS
    • 内存模型:线程--工作内存--save
    • volatile
      • 可用场景:
        • 1:运算过程没有用到变量的当前值,或只有单一线程修改变量值。
        • 2:不是和其他变量共同决定一件事(变量不需要与其它的状态变量共同参与不变约束)
      • 作用:
        • 1:变量对所有线程可见。线程在使用该变量前都要刷新,但java里的运算不是原子操作,因此volatile可以不一致。
        • 2:禁止指令重排序优化
    • 先行发生原则(由于指令重排序存在,就算满足先行发生原则,也不一定就会先被执行):
      • 1:同一个线程中是按代码顺序执行
      • 2:unlock先行发生于对同一个锁的lock操作。
      • 3:对一个volatile变量的写先行发生于后面对这个变量的读操作
      • 4:Thread对象的start()方法先行发生于此线程的每个动作
      • 5:线程所有操作都先行发生于对此线程的终止检测
      • 6:对线程的interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生
      • 7:一个对象的初始化完成(构造函数执行完)先行发生于它finalize()方法的开始
      • 8:操作A先行发生于操作B,操作B先行发生于操作C,那么得出A先行发生于C
    • 线程常用方法
      • sleep():当前线程什么也不做,但占用cpu资源
      • wait():阻塞当前线程,不占用cpu和临界资源
      • notify():在阻塞队列中唤醒一个线程去争锁
      • notifyall():唤醒阻塞队列所有线程去争锁
      • join():当前线程重要部分已执行完毕,获取另一个线程,调用它的join方法,表示让另一个线程join到这个线程中,执行完毕后再执行当前线程
      • yield():释放当前cpu的执行权,但释放之后又是公平竞争的机会。
    • 线程状态:
      • 1:新建:还没有调用start()方法
      • 2:运行:正在执行,或者是就绪状态
      • 3:无限等待:不会被CPU执行,要等待其它线程显式唤醒。wait()、join()和LockSupport.park()会进入无限等待。
      • 4:限期等待:不会被CPU执行,系统会自动唤醒,不需要显式唤醒。sleep()、设置了Timeout参数的wait()、设置了Timeout参数的join()、LockSupport.parkNanos()、LockSupport.parkUntil()
      • 5:阻塞:等待获取锁。
      • 6:结束
    • 乐观锁、悲观锁
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值