jvm本质上就是一种可以运行java字节码的虚拟机
jvm运行时的内存结构
根据java虚拟机规范规定,将内存分为了五个部分
线程私有的:程序计数器、java虚拟机栈、本地方法栈
线程共享的:堆、元空间(jdk1.7叫方法区)
程序计数器: 本质上就是一个指针,我们方法运行、执行命令,而每个命令其实都是有行数的,在执行命令的时候程序计数器会去纪录命令执行到了多少行,并记录代码执行的一个位置,主要用于在多线程情况下,因为线程任务没有执行完成就进行了CPU线程切换,通过程序计数器记录当前线程执行的一个位置,从而保证CPU切换回来时可以从切换之前的位置继续执行
java虚拟机栈: 生命周期和线程一致,每个方法在执行时都会创建一个栈帧,每一个方法从调用直至执行结束,就对应着一个栈帧从入栈到出栈的过程
本地方法栈:线程私有的,与java虚拟机栈作用基本相同,区别在于,本地方法栈加载的是带native修饰的方法(像底层由C语言实现的方法),而java虚拟机栈加载的是普通方法
native:在java中用native所修饰的方法,表示这个方法不是java原生的
堆: 在jvm中占用最大的一块内存空间的区域,主要用于存放我们的实例对象以及数组,也就是我们new出来的对象都存放在堆区,所以同时这里也成为了GC重点照顾的区域,因此也叫GC堆
元空间: 主要存储我们的.class信息,类的信息,方法的定义,常量,静态变量等
Java虚拟机规范中是这样定义方法区的:
它存储了每个类的结构信息,例如运行时常量池、字段、方法数据、构造函数和普通方法的字节码内容,还包括一些在类、实例、接口初始化时用到的特殊方法。
堆内存划分
堆内存主要又分为了两个部分年轻代和年老代
年轻代: 又被细分成了eden区和survivor0区和survivor1区,jvm默认的一个配置比列是Eden:from :to = 8:1:1
简单来说就是
java堆=年轻代+年老代
年轻代=eden+survivor0+survivor1
年老代: 经过年轻代多次垃圾回收存活下来的对象就放在年老代中
堆溢出: 首先我们的堆主要是用来存放对象实例的,也就是new的对象,堆溢出是指程序向某个堆块中写入的字节数超过了堆块本身可使用的字节数(之所以是可使用而不是用户申请的字节数,是因为堆管理器会对用户所申请的字节数进行调整,这也导致可利用的字节数都不小于用户申请的字节数),因而导致了数据溢出,并覆盖到物理相邻的高地址的下一个堆块。大白话就是堆溢出无外乎就是我们的堆内存空间已经不足于满足新对象实例的大小,从而产生的覆盖行为
一、jvm参数分类
根据jvm参数开头可以区分参数类型,共三类:“-”、“-X”、“-XX”,
标准参数(-):所有的JVM实现都必须实现这些参数的功能,而且向后兼容;
例子:-verbose:class,-verbose:gc,-verbose:jni……
非标准参数(-X):默认jvm实现这些参数的功能,但是并不保证所有jvm实现都满足,且不保证向后兼容;
例子:Xms20m,-Xmx20m,-Xmn20m,-Xss128k……
非Stable参数(-XX):此类参数各个jvm实现会有所不同,将来可能会随时取消,需要慎重使用;
例子:-XX:+PrintGCDetails,-XX:-UseParallelGC,-XX:+PrintGCTimeStamps……
二、关键参数详解
最重要和常见的几个参数如下:
■ -Xms20m :设置jvm初始化堆大小为20m,一般与-Xmx相同避免垃圾回收完成后jvm重新分。
■ -Xmx20m:设置jvm最大可用内存大小为20m。
■ -Xmn10m:设置新生代大小为20m。
■ -Xss128k:设置每个线程的栈大小为128k。
上面这几个参数我以前经常容易被混淆,不过后来根据字母拆分就简单了很多。
如下图:
还有几个GC的参数见名知意就不详解了,后面测试会一一说明,主要的如下:
■ -verbose:gc:可以输出每次GC的一些信息;
■ -XX:-UseConcMarkSweepGC:使用CMS收集器;
■ -XX:-UseParallelGC ;
■ -XX:-UseSerialGC;
■ -XX:CMSInitiatingOccupancyFraction=80 CMS gc,表示在老年代达到80%使用率时马上进行回收;
■ -XX:+printGC;
■ -XX:+PrintGCDetails:打印GC详情;
■ -XX:+PrintGCTimeStamps:打印时间戳;