- JDK:java 开发工具:包含javac、javap命令及JRE
- JRE:java运行环境:包含java常用的util、集合包和VM
- 一次编译,到处运行:比如下面Hellowworld.java文件通过javac命令编译成class文件,再通过java命令放到JVM虚拟机上运行,然后转化成不同系统的机器码运行(所以再Oricle官网下载JDK时需要选择不同的操作系统,他会屏蔽不同操作系统在底层硬件与指令上的区别),windows机器码为0101结构
- JVM虚拟机由类装载子系统、运行时数据区、字节码执行引擎三部分组成。class文件通过java命令由类装载子系统加载到内存区域、由执行引擎来执行class文件。
- 命令使用:以IDEA开发工具,ArrayDemo.java类为例
- 找到类右击,选择Open in Terminal
输入命令javac 类名.java(将java文件编译成class文件)
输入命令javap -c 类名.class(对代码进行反汇编,将16进制看不懂的class文件(官网可以查到16进制class文件对应的意思)汇编成可读的字节码
可以再通过命令javap -c 类名.class >类名.txt(导出txt文件来分析)
code字节码意思可以区去Orical官方JVM指令手册去查找对应意思,其实也就那几个,不难
例子:
-
对象都在堆中,其它区存放的都是对象的引用
-
栈:每个线程分别为一个栈帧,先进后出
-
局部变量表:存放局部变量,如果该局部变量是new出来的(比如main方法中创建一个对象),则该局部变量为一个指针,指向堆里该对象的位置
-
操作数栈:存放临时操作数的内存区域
-
方法出口:存放调用该方法的位置
-
程序计数器:存放当前线程执行代码的位置,由执行引擎对其修改。一条线程分配一块程序计数器内存
-
方法区:存放常量、静态变量、类信息(由类装载子系统装载进来的class文件,由字节码执行引擎执行该class文件)
-
本地方法栈:运行本地方法时,需要用到的一块存放区域(比如:new Thread()时,会调用start方法,而start会调用start0方法,该方法就是用native修饰的本地方法,该方法用C语言写的)
-
堆:
-
Minor GC
-
执行第一次Minor GC后,将非垃圾对象放到Survivor的From区同时这些对象的分代年龄+1(分代年龄保存在对象头里),然后对Eden区剩余的对象进行清除。第二次Minor GC的区域是Eden区和Survivor的From区,将非垃圾对象放到Survivor的To区同时分代年龄+1,然后对Eden区和From区进行垃圾清除。第三次Minor GC的区域是Eden区和Survivor的To区,将非垃圾对象放到Survivor的From区同时分代年龄+1,然后对Eden区和To区进行垃圾清除,然后Minor GC就是在第二次、第三次这样反复执行。当分代年龄达到15时,会被移到老年代区域(注意:分代年龄是可以设置的),被挪到老年代一般是静态变量的对象、线程池的对象、Spring容器的bean对象,随着程序的运行会一直存活在老年代。当老年代放满后,会执行full GC(对所有内存进行垃圾回收),执行full GC时会STW停掉用户的应用线程,出现卡顿现象(Minor GC也会STW,但时间非常端,用户几乎无感知)。当老年代满了,回收不了了会OOM,就是内存溢出
通过JDK自带的命令工具jvisualvm查看本机当前java进程
然后自动打开以下界面,通过此工具诊断java虚拟机内部运行情况
可以看到具体每块区域的运行情况
-
java虚拟机调优目的:减少STW
-
Java中Stop-The-World机制简称STW,是在执行垃圾收集算法时,java应用程序的其他所有线程都被挂起(除了垃圾收集帮助器之外)。Java中一种全局暂停现象,全局停顿,所有Java代码停止,native代码可以执行,但不能与JVM交互;这些现象多半是由于gc引起。
-
java调优:根据核心业务占用内存进行预估,再预设置,然后再调优。例子:
假定每秒1000单,分三台4核8G的服务器跑(一般情况分配3-4G给操作系统,所以最多虚拟机内存只能给4-5G),平均每台300单/秒
假如刚开始取值大小如下 -
每个线程设置为1M,300单/每秒(不包括订单查询等操作),因此就是300M/秒
-
比如分三个G给堆,按照堆正常老年代2/3,年轻代按8:1:1划分,数据如上。这eden区13秒就会满,假如每个订单1秒就完成,则在13秒时执行minor GC会有60M为非垃圾对象进入s区。此时在S区根据对象动态年龄判断,如果一批对象总大小超过S0/S1区50%,就直接进入老年代(可通过-XX:TargetSurvivorRatio设置),这样计算老年代将6分钟左右就满了,执行full GC后又基本清空。因此就可以在S区进行扩大,而扩大后仍然按照8:1:1的内存分配。这是如果给年轻代配2G,老年代1G,则结果如图
这样eden区大概25秒执行一次minor GC, S区也不会一次性超过一半。