zoukankan      html  css  js  c++  java
  • 读书笔记-实战jvm虚拟机

    第一章 初探java虚拟机

    系统虚拟机: Visual Box Vmware
    程序虚拟机: JVM

    类加载子系统: 负责从文件系统或者网络加载Class信息,加载的类信息放在了方法区,方法区还会存一些运行时的常量信息,包括字符串常量和数字常量
    java堆: 虚拟机启动时建立,是java程序最主要的工作区域,存放所有的类实例,堆空间是所有线程共享的
    直接内存: java中的nio库允许java程序使用直接内存, 读写频繁的场合适合使用直接内存
    垃圾回收系统: 对方法区,java堆和直接内存进行回收
    java栈: 每个线程有一个私有的java栈, java栈保存局部变量,方法参数等
    本地方法栈: 用于本地方法调用
    PC寄存器: 每个线程的私有空间,一般pc寄存器会指向当前正在被执行的指令,如果当前执行的是本地方法,则寄存器就是undefined

    • java堆
      常见构成:将java堆分成新生代+老年代
      新生代: eden区,s0区,s1区,也叫from区和to区
    public class SimpleHeap {
        private int id;
    
        public SimpleHeap(int id) {
            this.id = id;
        }
    
        public static void main(String[] args) {
            SimpleHeap s1 = new SimpleHeap(1);
            SimpleHeap s2 = new SimpleHeap(2);
            s1.show();
            s2.show();
        }
    
        public void show() {
            System.out.println("My ID is " + id);
        }
    }
    
    • 堆,方法区,栈的关系

    • 线程执行的基本行为是函数调用,每次函数调用的数据都是通过java栈传递的

    public class TestStackDeep {
        private static int count = 0;
    
        public static void recursion(long a, long b, long c) {
            long e = 1, f = 2, g = 3, h = 4, i = 5, k = 6, q = 7, x = 8, y = 9, z = 10;
            count++;
            recursion(a, b, c);
        }
    
        public static void recursion() {
            count++;
            recursion();
        }
    
        public static void main(String args[]) {
            try {
                recursion(0L, 0L, 0L);//1
                //recursion();//2
            } catch (Throwable e) {
                System.out.println("deep of calling = " + count);
                e.printStackTrace();
            }
        }
    }
    
    • 当方法中的参数比较多时,能够递归的层数就变少
    • 局部变量表: 用于保存函数的参数以及局部变量,函数变量表只在当前函数调用中有效,函数调用结束后,局部变量表就销毁
    • 相同栈容量下,局部变量少的函数可以支持更深的函数调用

    第三章 常用Java虚拟机参数

    • -XX:+PrintGC 简单的GC日志输出
    [GC (Allocation Failure)  53572K->8476K(182272K), 0.0054765 secs]
    [GC (Metadata GC Threshold)  50764K->9340K(178176K), 0.0085938 secs]
    [Full GC (Metadata GC Threshold)  9340K->8998K(139776K), 0.1062219 secs]
    
    • -XX:+PrintGCDetails 详细日志输出
    [GC (Allocation Failure) [PSYoungGen: 67696K->7344K(80896K)] 76693K->16341K(172032K), 0.0067267 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
    [GC (Metadata GC Threshold) [PSYoungGen: 29302K->7728K(81920K)] 38299K->16725K(173056K), 0.0072769 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
    [Full GC (Metadata GC Threshold) [PSYoungGen: 7728K->0K(81920K)] [ParOldGen: 8997K->16190K(141312K)] 16725K->16190K(223232K), [Metaspace: 35128K->35128K(1075200K)], 0.0834686 secs] [Times: user=0.50 sys=0.00, real=0.08 secs] 
    
    
    • 类加载/卸载 跟踪: 有的class文件是利用反射产生,无法通过文件系统找到

    • -XX:+TraceClassUnloading 和 -XX:+TraceClassLoading 跟踪类的加载和卸载过程

      首先加载Object类

      对Example类进行反复加载和卸载

    • -XX:+PrintVMOptions 程序运行时,打印虚拟机接收到的命令行参数

    输出格式:
    VM option '+PrintVMOptions'
    VM option '+PrintGC'
    
    • -XX:+PrintCommandLineFlags 可以打印显式和隐式参数,其中隐式参数是虚拟机自己设置的
    -XX:InitialHeapSize=199409344 -XX:MaxHeapSize=3190549504 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC 
    其中大部分参数都是虚拟机设置的
    
    • 新生代的配置:新生代的大小一般设置为整个堆空间的1/3到1/4左右

    • -XX:SurvivorRatio 用来设置新生代中eden空间和from/to空间比例关系 eg: -XX:SurvivorRatio=eden/from=eden/to

    • 使用不同的堆分配参数执行该段程序,虚拟机的表现不一样

    • 基本策略: 尽可能将对象留在新生代,减少老年代GC次数

    • -Xss 指定栈大小

    • 直接内存读写比jvm内存快,但是申请的时候比jvm内存慢

    • 虚拟机工作模式:client和server, 用java -version可以看到是哪种模式

    第四章 垃圾回收概念与算法

    引用计数法: 只要有任何一个对象引用了A,则A的引用计算器就加1,引用失效后就减1,只要A的引用计算器值为0,则A就不可能再被使用
    缺点: 1)无法统计循环引用 2)每次增加和减少引用都要伴随一个加法或者减法操作

    标记清除: 两个阶段: 标记阶段和清除阶段 标记:通过根节点,标记所有从根节点开始的可达对象, 清除:清除所有未被标记的对象
    缺点: 产生空间碎片

    复制算法: 将空间分为两块,每次只使用其中一块,垃圾回收时,将存活对象复制到未被使用的内存块中,然后清除原来的内存块

    • 一般复制算法用在年轻代,因为年轻代对象存活数量少,但是老年代不适合用复制算法,因为老年代存活的对象多,复制代价大

    标记压缩法: 从根节点出发,堆可达对象做标记,然后将这些对象压缩到内存的另外一边,将剩余的对象清除,可以避免碎片的产生,也不需要两块相同的内存空间

    • 四个级别的引用: 强引用,软引用,弱引用,虚引用
    1. 强引用: StringBuffer str = new StringBuffer("Hello World");
    2. 软引用:
    public class SoftRef {
        public static void main(String[] args) {
            User u = new User(1, "geym");
            SoftReference<User> userSoftRef = new SoftReference<User>(u);
            u = null;
    
            System.out.println(userSoftRef.get());
            System.gc();
            System.out.println("After GC:");
            System.out.println(userSoftRef.get());
    
            byte[] b = new byte[1024 * 925 * 7];
            System.gc();
            System.out.println(userSoftRef.get());
        }
    
        public static class User {
            public int id;
            public String name;
    
            public User(int id, String name) {
                this.id = id;
                this.name = name;
            }
    
            @Override
            public String toString() {
                return "[id=" + String.valueOf(id) + ",name=" + name + "]";
            }
        }
    }
    

    软引用会在内存不足的时候被回收

    • 每一个软引用都可以附带一个引用队列,当对象可达性发生改变时,由可达变成不可达,软引用对象会进入引用队列,通过这个引用队列,可以跟踪对象的回收情况

    弱引用-发现即回收,在系统GC时,只要发现弱引用,马上回收,由于垃圾回收器的线程级别低,不一定马上能回收,但是一旦开始回收,就会将这个对象放入引用队列中

    虚引用:随时都可以能被垃圾回收器回收,当试图通过get()方法取得强引用会失败
    当垃圾回收器准备回收一个对象时,如果发现还有虚引用,就会在回收对象后将这个虚引用加入到引用队列,以便通知程序对象回收情况
    如果软引用和弱引用被GC回收,JVM就会把这个引用加到引用队列里,如果是虚引用,在回收前就会被加到引用队列里。

    垃圾回收器种类

    1. 串行收集器
    2. 并行收集器
    3. CMS回收器
    4. G1回收器
    • 串行收集器: 使用单线程进行垃圾回收的回收器,每次回收只有一个工作线程,对并行性能弱的机器性能表现更好,
    • 串行收集器可以分为新生代和老年代串行回收器

    新生代串行回收器: 使用单线程回收,独占式回收,在回收的时候程序都要暂停,使用复制算法 -XX:+UseSerialGC 指定使用新生代和老年代串行收集器
    老年代串行回收器: 使用标记压缩算法,因为老年代对象比较多,所以启动后可能停顿时间比较长

    -XX:+UseSerialGC 新生代和老年代都用串行回收器
    -XX:+UseParNewGC 新生代用ParNew,老年代用串行回收器
    -XX:+UseParallelGC 新生代使用ParallelGC回收器,老年代用串行收集器
    -XX:+UseConcMarkSweepGC 新生代使用ParNew回收器,老年代用CMS

    新生代ParNew回收器: 只是简单的将串行收集器并行化

    新生代ParallelGC回收器:也是使用复制算法的回收器,比ParNew更关注吞吐量,支持自适应的GC调节策略

    -XX:+UseParallelGC: 新生代用ParallelGC 老年代用串行回收器
    -XX:+UseParallelOldGC: 新生代用ParallelGC回收器,老年代用ParallelOldGC回收器
    控制吞吐量的参数:
    -XX:MaxGCPauseMillis: 设置最大垃圾收集停顿时间
    -XX:GCTimeRatio: 设置吞吐量大小,0到100之间

    老年代ParallelOldGC回收器

    跟新生代的ParallelGC一样也是关注吞吐量的收集器: 使用标记压缩算法
    -XX:ParallelGCThreads: 设置垃圾回收时的线程数量

    CMS收集器: 主要关注停顿时间的收集器

    并发标记删除: 初始标记,并发标记,预清理,重新标记,并发清除,并发重置.其中初始标记,并发标记是独占系统资源,预清理,重新标记,并发清除,并发重置是可以和用户线程一起执行

    -XX:+UseConcMarkSweepGC 启用CMS回收器


    日志:
    [CMS-concurrent-abortable-preclean: 0.420/0.424 secs] [Times: user=0.42 sys=0.00, real=0.42 secs] 
    [GC (CMS Final Remark) [YG occupancy: 33152 K (59008 K)][Rescan (parallel) , 0.0021553 secs][weak refs processing, 0.0000082 secs][class unloading, 0.0003782 secs][scrub symbol table, 0.0003757 secs][scrub string table, 0.0001182 secs][1 CMS-remark: 141244K(150496K)] 174397K(209504K), 0.0031253 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
    [CMS-concurrent-sweep-start]
    
    • CMS-concurrent-abortable-preclean 表示进行一次新生代回收

    G1收集器: 为了替代CMS收集器

    • G1收集器使用分区算法,G1将堆分成一个个区,每次收集的时候只收集其中几个区域,所以可以控制每次收集的停顿时间
    • G1收集4个阶段:
    1. 新生代GC
    2. 并发标记周期
    3. 混合收集
    4. 如果需要,则进行FullGC


    2)

    相关参数汇总:





    第八章 锁与并发

    • 偏向锁: 一个锁被线程获取后,便进入偏向模式,当线程再次请求这个锁时无需再进行同步操作,如果在此期间有其他线程进行了锁请求,则退出偏向锁模式
    • -XX:+UseBiasedLocking 开启偏向锁
    public class Biased {
        public static List<Integer> numberList = new Vector<Integer>();
    
        public static void main(String[] args) throws InterruptedException {
            long begin = System.currentTimeMillis();
            int count = 0;
            int startnum = 0;
            while (count < 10000000) {
                numberList.add(startnum);
                startnum += 2;
                count++;
            }
            long end = System.currentTimeMillis();
            System.out.println(end - begin);
        }
    }
    
    


    启动参数:-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0 -client -Xmx512m -Xms512m


    • 自旋锁


    • 应用层锁优化策略
      1.只有在必要的时候才使用锁,减少使用锁的代码面积
      2.减少锁粒度,如使用ConcurrentHashMap
      3.锁分离

      4.锁粗化

    • 无锁

    • 原子操作

    • java内存模型:规范多线程下对共享资源的操作方法

    第九章 Class文件结构

  • 相关阅读:
    Vue.Js(html5) + Java实现文件分片上传
    进程、线程基础知识全家桶,30 张图一套带走
    20 张图揭开「内存管理」的迷雾,瞬间豁然开朗
    面试官:换人!他连 TCP 这几个参数都不懂
    TCP 半连接队列和全连接队列满了会发生什么?又该如何应对?
    实战!我用 Wireshark 让你“看得见“ TCP
    IP 基础知识全家桶,45 张图一套带走
    写了Bug,误执行 rm -fr /*,我删删删删库了,要跑路吗?
    你还在为 TCP 重传、滑动窗口、流量控制、拥塞控制发愁吗?看完图解就不愁了
    硬不硬你说了算!35 张图解被问千百遍的 TCP 三次握手和四次挥手面试题
  • 原文地址:https://www.cnblogs.com/Baronboy/p/14082240.html
Copyright © 2011-2022 走看看