JVM优化

这是一年前我Markdown的格式-。-

  • 参数类别

    • 标准参数

      • -client/-server()

        • x86

          • Windows系统,默认使用Client类型JVM
          • 其他系统,满足2G内存且有2核心,默认使用Server类型,否则Client类型
        • x64

          • 只有Server类型,不支持Client类型
        • 区别

          • Server的初始堆更大一些, 且使用并行GC, 启动慢运行快
          • Client初始堆小一些, 使用串行GC, __启动快__运行慢
      • -D: 可以给程序传入一些参数, java代码里通过System.getProperty(key)或getProperties()获取
    • -X参数

      • Running mode

        • -Xint interpreted mode

          • 启动快,执行慢
          • 强制JVM执行所有字节码,降低运行速度,降低10倍左右
        • -Xcomp complied mode

          • 启动慢,执行快
          • 启动时编译所有字节码为本地代码,带来最大程度优化,JIT不会根据代码类型自动判断是否编译
        • -Xmixed mixed mode

          • interpreted和complied混合使用,由JVM决定
    • -XX参数

      • boolean类型

        • -XX:+DisableExplicitGC
      • 非boolean类型

        • -XX:InitialHeapSize=111m / -Xms
        • -XX:MaxHeapSize=222m / -Xmx
        • -XX:PrintFlagsFinal / jinfo -flags {param} {pid}
  • 内存模型

    对于常量池, 1.6放在了方法区里, 1.7和1.8放在了Heep里

    • java7

      • Young

        • eden: 当eden区变满的时候,gc会将存活的对象移到survivor区
        • survivor: suvivor区有两个,同时只会有一个工作,另一个在gc回收时复制对象使用

          • S0
          • S1
      • Old: 大对象会直接进入Old Gen, 或在Survivor区经过几次垃圾回收仍然存活的对象也会进入此区域
      • Perm: 主要存放Class,Method,Field对象
    • java8

      • Young

        • Eden: 当eden区变满的时候,gc会将存活的对象移到Survivor区
        • Survivor: Suvivor区有两个,同时只会有一个工作,另一个在gc回收时复制对象使用

          • S0
          • S1
      • Old 大对象会直接进入Old Gen, 或在Survivor区经过几次垃圾回收仍然存活的对象也会进入此区域
      • MetaSpace

        • CSS
        • CodeCache
  • JDK提供的常用命令

    • jstat - Monitors Java Virtual Machine (JVM) statistics.常用于查看__堆内部__内存使用情况

      • 便于实时显示可以加上 1000(毫秒) 0(显示次数, 非正数则无限次) - 每1秒打印一次
      • -class pid: 查看类的加载信息
      • -compiler pid: 查看类的编译信息
      • -gc pid: 查看类的编译信息
      • -gcutil pid: 可以查看堆内存占用率和回收时间
    • jmap - Prints shared object memory maps or heap memory details for a process, core file, or remote debug server.可以查看比jstat更详细的内存统计

      • -heap pid: 查看堆内存的使用情况
      • -histo[:live] pid: histogram直方图显示所有对象以及占用空间, :live可以只查看活跃对象
      • -__dump__:[live,] format=b, file=filename pid: Dump jvm当前内存到文件中
    • jstack pid - 用于查看线程状态
    • jvisualvm - JDK[6],7,8提供的一个图形化整合以上所有命令的工具, 可远程

      • 远程配置: 可加上以下OPTS

        • -Dcom.sun.management.jmxremote # 开启JMX
        • -Dcom.sun.management.jmxremote.port=9999 # 指定通信端口
        • -Dcom.sun.management.jmxremote.authenticate=false # 关闭auth
        • -Dcom.sun.management.jmxremote.ssl=false # 关闭SSL
  • 对内存dump进行分析

    • 1. jhat [options] dumpfile: 提供了一个简单的Web页面便于查询对象情况(使用OQL查询)

      • -port port-number: 指定绑定的Web端口
      • -exclude exclude-file: 指定文件排除一些数据成员, 格式(java.lang.String.value)
      • -debug int: 指定debug级别默认0, 向上调整
    • 2. MAT: 一个基于Eclipse的软件, 很强大, Eclipse.....这里还是不作介绍了

  • 垃圾回收

    • 垃圾回收常见算法

      • 1.引用计数算法: 在每个对象内部维护一个计数器, 每当被引用+1, 当计数器归0, 回收, 不能解决循环依赖回收

      • 2.标记清除算法: 从根节点遍历所有对象, 标记所有被Root(间接)引用的对象, 标记后释放所有未被标记的 , 耗时,内存碎片化

      • 3.标记压缩算法: 在二的基础上把标记的对象复制到内存一侧形成连续, 释放临界值以外所有对象, 耗时

      • 4.复制算法

      • 5.分代算法

最近搬家, 宽带老贵了, 于是只能帮帮邻居做做安全工作-。-, 周末回家发现身边连个U盘都没有, 并且没USB网卡(就不摆弄虚拟化了), Windows正好又崩了(万恶的Win, 要不是为了打JX3)
只能用Grub了, 好了直接上配置, 我这里用的是Debian系的Kali, 这里引导的是LiveCD, 如想要安装(非一次性引导), 可以把live改成你Image内的安装文件夹(可能是/install)

menuentry "Kali live"{
    # 指定Image所在卷(hd1,gpt4)以及位置(/xxxx/xx.iso), 我这里是EFI GPT的分区
    loopback loop (hd1,gpt4)/home/wars/Downloads/Aria2/kali-linux-2019.2-amd64.iso
    # 指定内核(/live/vmlinuz), 内核所在目录(live)和内核所在Image位置(/xxxxx/xx.iso)
    linux (loop)/live/vmlinuz boot=live iso-scan/filename=/home/wars/Downloads/Aria2/kali-linux-2019.2-amd64.iso splash
    echo 'Loading Linux kernel ...++++++++++++++++++++++++++++++++++++++++++++++++++++++++++'
    # 指定ramdisk基于Image根路径的相对位置
    initrd (loop)/live/initrd.img
    echo 'Loading initial ramdisk ...======================================================================'
}

举例, 0x80000001

    int num = 0x80000001;
    printf("%d", num);
    /*
    * 存进内存过程
    * 1. 转成二进制, **正数符号位不变(负数符号位取反)**
    *    1000 0000 0000 0000 0000 0000 0000 0001
    * 2. 源数据为正数, 正数补码不变, 最后内存中即
    *    1000 0000 0000 0000 0000 0000 0000 0001
    * printf过程
    * 1. 补码
    *    1000 0000 0000 0000 0000 0000 0000 0001
    * 2. 符号位为1, 转反码
    *    1111 1111 1111 1111 1111 1111 1111 1110
    * 3. 符号位为1, 转原码
    *      1111 1111 1111 1111 1111 1111 1111 1111
    * 4. 交给printf, %d是有符号打印, 结果为
    *      -2147483647
    */

总结存储流程:

  1. 把数据转成二进制, 负数最左侧位取反
  2. 根据源数据正负决定是否需要转补码(源数据是否为负数)

SELinux

MySQL配置文件中修改了datadir后无法启动, log大致为

    Can't create test file /datadir/boxy.lower-test

很确定没有权限问题, 最后发现是selinux的问题, 自然不能关掉它。

datadir为修改后的数据目录,新建一条规则,指定/datadir目录及其下的所有文件的扩展属性为mysqld_db_t, 此外还有mysqld_log_t、mysqld_etc_t、mysqld_port_t这样

    #semanage fcontext -a -t mysqld_db_t "/datadir(/.*)?"
    #restorecon -Rv /datadir

这里可能需要重启, 貌似还有个即时生效的命令chcon, 未深究.

Client Sock

如果同时修改了sock的目录, 想要本机客户端命令正常工作, 需要在客户端配置文件标签(标签已列出)中同时加上

[client]
socket=/xxxxx
[client-mariadb]
socket=/xxxxx
[mysql]
socket=/xxxxx
[mysql_upgrade]
socket=/xxxxx
[mysqladmin]
socket=/xxxxx
[mysqlbinlog]
socket=/xxxxx
[mysqlcheck]
socket=/xxxxx
[mysqldump]
socket=/xxxxx
[mysqlimport]
socket=/xxxxx
[mysqlshow]
socket=/xxxxx
[mysqlslap]
socket=/xxxxx

如果还有一些命令工作不正常(如mysql_secure_installation), 可在命令后加-S /xx/xxxxx.sock

结束

参考

  1. selinux-and-mysql

Concurrency

CountDownLatch

    CountDownLatch countdownlatch = new CountDownLatch(1); // The difinition count is 1
    System.out.println("Count: " + countdownlatch.getCount()); // 1

    new Thread(() -> {
        try {
            countdownlatch.await(); // Current thread enter await, until count reduced to 0
            System.out.println("Count: " + countdownlatch.getCount()); // 0
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }).start();

    Thread.sleep(1000);
    System.out.println("Count down");
    countdownlatch.countDown(); // down 1

CyclicBarrier

    public class CyclicBarrierExample {

        private static LongAdder count = new LongAdder();
        private static ExecutorService executorService = Executors.newFixedThreadPool(5);
        private static CyclicBarrier cyclicBarrier = new CyclicBarrier(5, () -> System.out.println(count.longValue()));

        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                executorService.execute(CyclicBarrierExample::add);
            }
            executorService.shutdown();
        }
        
        private static void add() {
            try {
                count.increment();
                cyclicBarrier.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }

Semaphore

    Semaphore semaphore = new Semaphore(5);
    for (int i = 0; i < 5; i++) {
        new Thread(() -> {
            try {
                semaphore.acquire(); // Get a semaphore
                semaphore.release(); // Release a semaphore
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
       Thread.sleep(1000);
    System.out.println("Available semaphore: " + semaphore.availablePermits()); // 5
    Semaphore semaphore = new Semaphore(5);
    for (int i = 0; i < 5; i++) {
        new Thread(() -> {
            try {
                semaphore.acquire();
                //semaphore.release(); // Not release
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
    Thread.sleep(1000);
    System.out.println("Available semaphore: " + semaphore.availablePermits()); // 0
    Semaphore semaphore = new Semaphore(5);
    for (int i = 0; i < 5; i++) {
        new Thread(() -> {
            try {
                semaphore.acquire();
                //semaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
    Thread.sleep(1000);
    System.out.println("Available semaphore: " + semaphore.availablePermits()); // 0
    semaphore.acquire(); // Because available semaphore is 0, threa enters await status, until available gt 0
    System.out.println("END"); // Always not output END

Atomic

AtomicLong and LongAdder

Jdk8 update LongAdder and DoubleAdder

Under low update contention, the two classes have similar characteristics. But under
high contention, expected throughput of this class is significantly higher, at the expense of higher space consumption.

Recommend use 'LongAdder' and 'DoubleAdder',
But may not be safe of reading(update value when reading), AtomicLong is safer, its principle is CAS(compareAndSwap)

AtomicXX

@Slf4j
@ThreadSafe
    public class AtomicTest {
        
        private final static int threadTotal = 200;
        private final static int clientTotal = 5000;
        // private static AtomicLong count = new AtomicLong(0);
        private static LongAdder count = new LongAdder(); //Initial value is 0

        public static void main(String[] args) throws Exception {
            ExecutorService executor = Executors.newCachedThreadPool();
            Semaphore semaphore = new Semaphore(threadTotal);
            CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
            log.info("CountdownLatch count: {}", countDownLatch.getCount());

            new Thread(() -> {
                try {
                    countDownLatch.await();
                    executor.shutdown();
                    log.info("CountdownLatch count: {}", countDownLatch.getCount());
                    log.info("count: {}", count.longValue());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }).start();

            Thread.sleep(1000);
            for (int i = 0; i < clientTotal; i++) {
                executor.execute(() -> {
                    try {
                        semaphore.acquire();
                        add();
                        semaphore.release();
                        countDownLatch.countDown();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
            }
        }

        private static void add() {
            count.increment();
        }
    }

AtomicReference

An object reference that may be updated atomically.

    public class AtomicReferenceTest {

        @Data
        private static class User {
            private String name;
        }

        public static void main(String[] args) {
            AtomicReference<User> longAtomicReference = new AtomicReference<>();
            User oldUser = new User();
            oldUser.setName("old");
            longAtomicReference.set(oldUser);

            User userBak = oldUser; // Copy user addr
            oldUser = new User();

            boolean b = longAtomicReference.compareAndSet(oldUser, userBak); // Check addr value, if equals, update it
            System.out.println(b);
        }
    }

AtomicReferenceFieldUpdater

Modify object field

    public class AtomicReferenceFieldUpdaterTest {
    
        @Data
        private static class User {
            volatile String name;
        }

        public static void main(String[] args) {
            AtomicReferenceFieldUpdater<User, String> atomicReferenceFieldUpdater = AtomicReferenceFieldUpdater.newUpdater(User.class, String.class, "name");
            User user = new User();
            user.setName("Wars");

            atomicReferenceFieldUpdater.compareAndSet(user, user.getName(), "Cat");
            System.out.println(user.getName());
        }
    }

AtomicStampedReference

AtomicStampedReference vs. AtomicReference added one version number param, fix ABA bug

    public class AtomicStampedReferenceTest {

        private static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(0, 0);

        public static void main(String[] args) {
            atomicStampedReference.compareAndSet(0, 1, 0, 1);
        }
    }

AtomicLongArray

AtomicLongArray through the index operation array

    public class AtomicLongArrayTest {

        private static AtomicLongArray atomicLongArray = new AtomicLongArray(new long[]{1L, 2L, 3L});
        
        public static void main(String[] args) {
            atomicLongArray.incrementAndGet(0); // index
        }
    }

Synchronized

  • Code scope

    • If synchronized(this), not unique: The current instance is valid
    • If synchronized(A.class), unique: The all instance is valid
  • Function

    • equals synchronized(this){} this function content
    • The current instance is valid
  • Static function

    • equals synchronized(This.class){} this function content
    • The all instance is valid
  • Class

    • equals synchronized(This.class){} all function content of this class
    • The all instance is valid

volatile

Guaranteed single thread, sequential execution of reads and writes
1) Wrong usage

    @NotThreadSafe
    public class VolatileTest {

        private static final int MAX_THREAD = 50;
        private static final int EXECUTE_COUNT = 5000;
        private static volatile int count = 0;

        public static void main(String[] args) {
            CountDownLatch countDownLatch = new CountDownLatch(EXECUTE_COUNT);
            // Executed out
            new Thread(() -> {
                try {
                    countDownLatch.await();
                    System.out.println("count: " + count);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();

            // Executor
            ExecutorService executor = Executors.newCachedThreadPool();
            Semaphore semaphore = new Semaphore(MAX_THREAD);
            for (int i = 0; i < EXECUTE_COUNT; i++) {
                executor.execute(() -> {
                    try {
                        semaphore.acquire();
                        count++;
                        semaphore.release();
                        countDownLatch.countDown();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
            }
        }
    }

2) Recommended usage

    public class VolatileTest2 {

        private static volatile boolean semaphore = false;

        public static void main(String[] args) throws InterruptedException {
            new Thread(() -> {
                try {
                    while (!semaphore) Thread.sleep(1000);
                    System.out.println("Finish");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();


            Thread.sleep(10000);
            semaphore = true;
        }
    }

Singleton publish

  1. Dual if lazy mode
    @ThreadSafe
    public class A_Singleton {

        private volatile static A_Singleton instance;

        private A_Singleton() {
        }

        public static A_Singleton getInstance() {
            if (null == instance)
                synchronized (A_Singleton.class) {
                    instance = new A_Singleton(); // If no volatile, it may lead to Out-of-order execution initialization
                }

            return instance;
        }
    }
  1. Static scope Hungry mode
    @ThreadSafe
    public class B {

        private static B instance;

        static {
            instance = new B();
        }

        private B() {
        }

        public static B getInstance() {
            return instance;
        }
    }
  1. Enum mode
    enum E {
        INSTANCE;

        private C_EnumMode instance;

        E() {
            instance = new C_EnumMode();
        }

        public C_EnumMode getInstance() {
            return instance;
        }
    }