JVM随笔

2016年04月23日

JAVA文档

https://docs.oracle.com/javase/tutorial/essential/index.html

内存信息dump

    # 导出整个JVM 中内存信息
    jmap -dump:format=b,file=文件名 [pid]

    # 查看JVM堆中对象详细占用情况
    jmap -histo [pid]

    # dump内容分析,默认端口 7000
    jhat -J-Xmx1024M [file]

查看JVM默认启动参数

  java -XX:+PrintFlagsFinal -version

设置方法区(永久代)大小

  -XX:PermSize=10M -XX:MaxPermSize=10M

在JDK1.8之后使用 -XX:MaxMetaspaceSize=128m 设置元空间大小(JVM参数PermSize 和 MaxPermSize会被忽略,当前在启动时会有警告信息)

设置GC日志输出

    -XX:+PrintGC  简略信息输出
    -XX:+PrintGCDetails    详细信息输出
    -XX:+PrintGCTimeStamps   gc时间打印
    -XX:+PrintGCApplicationStoppedTime   停顿时间
    -XX:+PrintGCApplicationConcurrentTime 打印每次垃圾回收前,程序未中断的执行时间
    -XX:+PrintHeapAtGC	打印GC前后的详细堆栈信息
    -Xloggc:filename	把相关日志信息记录到文件以便分析.
    -XX:+PrintClassHistogram 垃圾回收前打印类信息柱状图
    -XX:+PrintTenuringDistribution	  查看每次minor GC后新的存活周期的阈值
    -XX:+PrintTLAB   查看TLAB空间的使用情况

GC日志样例:

2019-04-03T17:25:10.175+0800: 4.311: [GC (Allocation Failure) 895844K->20823K(1363968K), 0.0159384 secs] 2019-04-03T17:25:10.415+0800: 4.551: [GC (Metadata GC Threshold) 394193K->22130K(1810944K), 0.0099884 secs] 2019-04-03T17:25:10.425+0800: 4.561: [Full GC (Metadata GC Threshold) 22130K->15705K(1925632K), 0.0482205 secs]

Metadata GC Threshold 代表 Metaspace 不够了,MetaspaceSize默认只有21M左右。

metaspace说明:传送门,简单来说就是用 Metaspace 代替了永久区。

查看当前jvm实例使用的垃圾收集器

      jmap -heap pid

输出结果中,using thread-local object allocation. 下面一行就是当前使用的垃圾收集器

JDK8 默认堆大小

初始化 128M,最大2G,且默认使用 Parallel 垃圾收集器(标记整理),初始化栈大小为1M。

uintx InitialHeapSize := 134217728 uintx MaxHeapSize := 2147483648 bool UseParallelGC := true bool UseParallelOldGC = true bool UseAdaptiveSizePolicy = true intx ThreadStackSize = 1024 bool UseConcMarkSweepGC = false uintx MetaspaceSize = 21807104

且默认打开了 UseAdaptiveSizePolicy 配置,用于动态调整Eden和Survivor比例,进入老年代对象年龄等。

使用 jcmd pid VM.flags 查看当前java进程的启动参数(或者指定查看参数:jinfo -flag ThreadStackSize pid)

使用 -Xss2M 指定 线程栈大小。

sunhotspot jvm和ibmjvm中都把引用实现为一个指针,因此在64位平台上,占8个字节,在32位平台上占4个字节。

-XX:+UseCompressedOops 默认为true,开启指针压缩,这样 java引用就只占4个字节,若为开启则在64位机器上引用会占8个字节。

JDK8元空间说明:

http://caoyaojun1988-163-com.iteye.com/blog/1969853

http://www.infoq.com/cn/articles/Java-PERMGEN-Removed

http://www.cnblogs.com/redcreen/archive/2011/05/04/2037057.html

G1垃圾收集器GC日志说明

https://blogs.oracle.com/poonam/understanding-g1-gc-logs

arraylist get(负数) 抛出 IndexOutOfBoundsException 没有栈异常信息

long 转 int 可能为负数

3421665166L 强转为 int 输出为 -873302130

fullGC 过多 导致 用户线程暂停,zk心跳异常

日志打了一半,后续既没有info日志,也没有异常日志

jar 包 release 版本以前可以覆盖,导致本地仓库的jar包是老版本,使用rources 包是最新的,查看包的源码是没有问题的,

但是运行一段时间后,执行到调用该方法的地方,则报错,无此方法。

JSON 相关框架性能比较

整体来说,小对象的序列化和反序列化 ,gson表现最好,大对象的序列化和反序列化, jackson 是更好的选择,fastjson 虽然也在速度上也不错,但其功能完备性相对较差

http://www.cnblogs.com/java-class/p/6653735.html http://www.bijishequ.com/detail/373398

fastjson 普及性的问题:

https://www.zhihu.com/question/44199956

spring事务方法内部调用生效

使用 AopContext.currentProxy() 直接获取代理对象,执行方法调用

并在配置中指定

    <property name="exposeProxy">  
                <value>true</value>  
            </property

多个事务管理器,默认使用的事务管理器

多个事务管理器,默认使用的事务管理器,根据加载顺序决定,第一个加载到的事务管理器,作为默认事务管理器

@ComponentScan 注解 默认包扫描

如果 basePackages 未配置,则默认从使用这个注解的类所在的包开始扫描

下面的调用,在找不到 NotSupportedException (此类由运行时加载,运行环境中可能找不到) 时会抛出异常:

    public final class Test {

      private Test() throws NotSupportedException {
        throw new NotSupportedException("不可实例化");
      }

      public static void main(String[] args) {
        System.out.println(11);
      }

Bean copy 性能比较

Apache Beanutils 性能最差 cglib性能最好 spring 的 BeanUtils 居中

JDK7 的 Try-with-resources

下面代码可以自动关闭实现了java.lang.AutoCloseable或java.io.Closeable的对象资源

    try (BufferedReader br = new BufferedReader(new FileReader(path))) {  
          return br.readLine();  
       }  

桥接方法(bridge method)

https://blog.csdn.net/mhmyqn/article/details/47342577

GC选择

https://groups.google.com/a/jclarity.com/forum/#!topic/friends/0Ennhw9n0Hg http://eivindw.github.io/2016/01/08/comparing-gc-collectors.html

javax.annotation.processing.Processor 使用

https://www.race604.com/annotation-processing/

java interface 方法上声明的注解无法被实现类继承

多接口实现问题,所以interface 方法上声明的注解无法被实现类继承

https://stackoverflow.com/questions/4745798/why-java-classes-do-not-inherit-annotations-from-implemented-interfaces

分布式自增ID

twitter 雪花算法 snow flake

spring应用启动很慢

eureka在启动时会去查当前机器的域名,如果域名解析过慢,导致整个应用启动都很慢,将本机的ip和域名加到 /etc/hosts 里面,这样启动就快了,从 3min 缩短到 20s

UUID.randomUUID() 生成重复id问题

https://stackoverflow.com/questions/35050628/repeated-set-of-uuids-from-javas-uuid-randomuuid

protobuf 和 protostuff

protobuf 是与语言无关、平台无关的通信协议,需要写pb文件。

protostuff 是一个java类库,可直接对普通的javabean进行序列化和反序列化,支持多种序列化方式(protobuf, json, yaml, protostuff 等)

jpa 和 mybatis 的比较

http://www.spring4all.com/article/391

java 负数取模

先忽略负号,按照正数运算之后,被取模的数是正数结果就取正,反之取负。

  • (-2)%5 = -2
  • 2%(-5) = 2
  • 5%(-2) = 1
  • (-5)%2 = -1

jdk 1.7 concurrentHashMap 弱一致性

concurrentHashMap并不能完全替代HashTable,因为 HashTable 是强一致性,而concurrentHashMap是弱一致性

传送门

限流算法

  • 计数器
  • 漏桶算法
  • 令牌桶算法: guava 的 RateLimiter提供了令牌桶算法实现

JDK8 Stream 并行处理

在jdk8中,可以调用 parallel() 方法使最终的终端操作变为 并行化 处理,那么JDK中是如何做的呢?有两种情况:

  1. 每个 Stream流 都使用 一个 新的线程池(那么多线程环境下,多个线程都使用 parallel 提高并行度,会不会导致线程爆炸,反而降低了并发效率呢?)
  2. 所有 Stream流 都使用 一个 共享的线程池 (那么多个线程同时使用 parallel,会不会导致 一个线程的Stream流提交的多线程任务过多,从而影响了另外一个毫不相干的线程的Stream流的parallel执行,使其阻塞呢?)

带着上面两个问题,调试查看Stream的parallel执行过程,发现所有Stream流的并行操作都是共享 java.util.concurrent.ForkJoinPool#common (工作线程默认为CPU个数) 这个线程池来进行并发操作的。所以也就是上面说的情况2.

tomcat 最大连接数

https://blog.csdn.net/quliuwuyiz/article/details/79979031

jdk8 比较器链式调用疑问

// 方法一
Comparator<List<Integer>> c1 = Comparator.comparing(List::size);
c1 = c1.reversed();

// 方法二
Comparator<List<Integer>> c2 = Comparator.comparing(List::size).reversed();

// 方法三
Comparator<List<Integer>> c3 = Comparator.comparing(List<Integer>::size).reversed();

使用方法二编码方式,它是无法通过编译的(报错:Not-static method cannot be referenced from a static context.),只有一和三可以通过编译。

详情:https://bugs.openjdk.java.net/browse/JDK-8190548

java 安全访问模式

https://www.ibm.com/developerworks/cn/java/j-lo-javasecurity/index.html

卫语句

卫语句就是把复杂的条件表达式拆分成多个条件表达式,比如一个很复杂的表达式,嵌套了好几层的if - then-else语句,转换为多个if语句,实现它的逻辑,这多条的if语句就是卫语句.

Java类加载器

System, current, context? Which ClassLoader should you use?

Do You Really Get Classloaders?

IBM开发者文档

https://www.ibm.com/developerworks/cn/

PicoContainer 实例化任意对象的框架

https://www.cnblogs.com/yaoxiaohui/archive/2009/03/08/1406228.html

JDBI,JOOQ

都是 持久层框架,类似于Mybatis,JDBI文档:传送门,JOOQ文档:传送门

使用 JOOQ的流式sql拼接来构建sql,使用JDBI绑定参数执行SQL, 例子

分布式事务

https://www.ktanx.com/blog/p/2862

volatile 分析

http://ifeve.com/volatile/

JVM栈异常信息优化

传送门

JVM中有个参数:OmitStackTraceInFastThrow,字面意思是省略异常栈信息从而快速抛出,那么JVM是如何做到快速抛出的呢?JVM对一些特定的异常类型做了Fast Throw优化,如果检测到在代码里某个位置连续多次抛出同一类型异常的话,C2会决定用Fast Throw方式来抛出异常,而异常Trace即详细的异常栈信息会被清空。这种异常抛出速度非常快,因为不需要在堆里分配内存,也不需要构造完整的异常栈信息.

字符集

http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html

Little’s Law(利特尔法则)

在一个稳定的系统中,长时间观察到的平均顾客数量L,等于,长时间观察到的有效到达速率λ与平均每个顾客在系统中花费的时间之乘积,即L = λW

可用来估算 线程池等待队列 平均等待数量

浮点数表示法

float 和 double 采纳了 IEEE 754 标准(用科学记数法以底数为 2 的小数来表示浮点数)中所定义的单精度 32 位浮点数和双精度 64 位浮点数的格式

V=(-1)^s * M * 2^E (公式1) (1)(-1)^s表示符号位,当s=0,V为正数;当s=1,V为负数。 (2)M表示有效数字,大于等于1,小于2。 (3)2^E表示指数位。 对于32位的float,s占1位,E占8位,M占32位,

float从左到右,第一位是符号位,2-9位共8位表示整数位,2的8-1次方等于128,后面23位是表示小数的,所以最大值是2^128-1。 double从左到右,第一位是符号位,2-12是共11位表示整数位,2的11-1次方等于1024。剩余20位表示小数,所以最大值是2^1024-1。

https://blog.csdn.net/lovelvyan/article/details/52300137

double和float都是精度:传送门

java中的编码

java每个char使用2个字节,采用unicode字符集,使用utf-16编码方式。

所以如果想在代码中使用超过2个字节表示的unicode字符(例如 0x1F344),则使用UTF-16进行表示(前一个字符表示为 \uD83C\uDF44).

相关文章: how does one show extended unicode > 0xFFFF

使用 Java 语言进行 Unicode 代理编程

java unicode 说明

base64编码

http://www.ruanyifeng.com/blog/2008/06/base64.html

逃逸分析优化

以下代码,打开逃逸分析(-XX:+DoEscapeAnalysis)花费10ms左右,若关闭逃逸分析(-XX:-DoEscapeAnalysis)花费100ms左右

      // -server -Xmx10m -Xms10m  -XX:-DoEscapeAnalysis -XX:+PrintGC -Xss160k
      public static void main(String[] args) {

          // 设置 -XX:+DoEscapeAnalysis 做逃逸分析,在栈上分配空间
          try {
            long start = System.currentTimeMillis();
            for (int i = 0; i < 10000000; i++) {
              alloc();
            }
            System.out.println(System.currentTimeMillis() - start);
          } catch (Exception e) {
            e.printStackTrace();
          }

        }

        private static void alloc() {
          byte[] b = new byte[2];
          b[0] = 1;
        }

字节序

理解字节序

  • 大端字节序:高位字节在前,低位字节在后,这是人类读写数值的方法。
  • 小端字节序:低位字节在前,高位字节在后。

设计模式

https://github.com/jpsoroulas/java8-patterns

接口方法参数上的注解无法被实现类对应方法的参数继承到, 方法同理

java 对象占多少个字节

一个Java对象到底占多大内存?

深入分析Volatile的实现原理

深入分析Volatile的实现原理

Volatile 变量的写,在汇编层面会增加 lock 指令,lock前缀的指令在多核处理器下会引发了两件事情:

  • 将当前处理器缓存行的数据会写回到系统内存。
  • 这个写回内存的操作会引起在其他CPU里缓存了该内存地址的数据无效。

Compressed Oops

开启普通对象指针压缩: -XX:+UseCompressedOops , jdk8默认为 开启

HotSpot 指针压缩优化 :传送门

Oops : ordinary object pointer

JVM优化之压缩普通对象指针

What does the UseCompressedOops JVM flag do and when should I use it?

上面的问题回答中提出,指针压缩后引用是4个字节,可以管理32G内存,原因是:4个字段可以表示4G个地址,而JVM是使用8字节填充的,每个地址对少占8个字节,所以可以管理32G内存。

java线程的工作内存

线程的working memory是cpu的寄存器和高速缓存的抽象描述:现在的计算机,cpu在计算的时候,并不总是从内存读取数据,它的数据读取顺序优先级 是:寄存器-高速缓存-内存。

高速缓存又分为一级Cache(L1 Cache)和二级Cache(L2 Cache),L1 Cache集成在CPU内部,L2 Cache早期一般是焊在主板上,现在也都集成在CPU内部,常见的容量有256KB或512KB L2 Cache。(现在一些还有L3 Cache)

计算机中内存、cache和寄存器之间的关系及区别

CLH 自旋锁

CLH(Craig, Landin, and Hagersten locks): 是一个自旋锁,能确保无饥饿性,提供先来先服务的公平性。

CLH锁也是一种基于链表的可扩展、高性能、公平的自旋锁,申请线程只在本地变量上自旋,它不断轮询前驱的状态,如果发现前驱释放了锁就结束自旋。

当一个线程需要获取锁时:

1.创建一个的QNode,将其中的locked设置为true表示需要获取锁

2.线程对tail域调用getAndSet方法,使自己成为队列的尾部,同时获取一个指向其前趋结点的引用myPred

3.该线程就在前趋结点的locked字段上旋转,直到前趋结点释放锁

4.当一个线程需要释放锁时,将当前结点的locked域设置为false,同时回收前趋结点

K8S 在线书籍

https://jimmysong.io/kubernetes-handbook/