先了解一下基本概念:
JVM:java Virtual Mechinal(Java虚拟机)
jvm = 一次编写,到处运行
jvm:翻译功能,代码翻译成机器能识别的文件
JRE:java Runtime enviroment(java运行时环境)
jre= jvm + java核心类库(开发常用的类) + 支持文件
JDK:java development kit(java开发工具包)
jdk = jre + java工具(包括一些监控工具) + java基础类库
JVM–运行时数据区
java虚拟机在执行java程序的过程中会把它所管理的内存划分为若干个不同的数据区域
类型
程序计数器
运行时数据区域--程序计数器
指向当前线程正在执行的字节码指令的地址或者行号
原因:多个线程同时执行,cpu来回切换正在执行的线程,所有每次记住切换线程的时候需要记住当前执行的行号或者地址
虚拟机栈
存储当前运行方法时所需要的数据,指令,返回地址
栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息。
三个概念
栈帧: 一个方法的运行空间
栈的FIFO : 先进后出
-Xss: 每个线程的栈大小
虚拟机的入栈出栈
1、局部变量表:
一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量
2、操作数栈
方法的执行就对应着栈帧在虚拟机栈中入栈和出栈的过程(操作数栈)
注:
将demo文件反编译,输出到a.txt文件中
javap -v Demo.class >> a.txt
对应区域
可以理解成,压栈是为了转换,出栈是存储局部变量表
对应指令
具体流程
JAVA虚拟机栈的最小单位可以理解为一个个栈帧,一个方法对应一个栈帧,一个栈帧可以执行很多指令,下图就是一个栈帧,包括:局部变量表,操作数帧,动态链接,方法出口等。
注:动态链接:
方法中有对象的时候,指向对象的实例(如:多态)
3、本地方法栈
本地方法栈和虚拟机栈类似,本地方法栈服务的是Native方法(本地方法)。
jvm中的本地方法是指方法的修饰符是带有native的但是方法体不是用java代码写的一类方法,这类方法存在的意义当然是填补java代码不方便实现的缺陷而提出的。
作用同java虚拟机栈类似
区别是:虚拟机栈为虚拟机执行Java方法服务,而本地方法栈则是为虚拟机使用到的Native方法服务。(3)是线程私有的,它的生命周期与线程相同,每个线程都有一个。
如Object 的hashCode()方法,则虚拟机开辟本地方法栈来执行
以上程序计数器,虚拟机栈,本地方法栈三个是线程独有/私有的
堆
方法区
如下图:每一个业务一个线程,都会创建自己的程序计数器,虚拟机栈,本地方法栈,但是他们共用堆和方法区。
虚拟机共有的数据区域-方法区和堆
方法区:
jdk1.7以前存放:类信息,常量,静态变量,即时编译器编译后的代码等数据(JIT)
jdk1.7以后:类信息,静态变量(常量和JIT存放在堆 )
堆(Heap)
所有的对象实例和数组都在堆上分配
堆大小设置:启动时分配的内存 -Xms, 最大内存 -Xmx
JMM内存模型
JMM与JVM运行时数据区的区别?
JMM是为了解决多线程对共享数据访问保持一致性
JMM中的三个代
新生代 、老年代、永久代(永久代,1.7以前是jvm划分的,jdk1.8称为元空间,用的是系统的空间)
堆:新生代、老年代
新生代:主要是用来存放新生的对象
老年代:主要存放应用程序中生命周期长的内存对象
方法区:永久代
永久代:内存的永久保存区域
JMM它只对共享的数据进行了划分,而JVM对运行时数据区进行划分(面更广)。
GC是什么
自动内存管理和垃圾清理机制,概括的说,该机制对JVM中内存进行标记,并确定那些内存需要回收。
Minor GC:
当JVM无法再为一个新的对象分配空间时会触发,不会影响到老年代,哪怕没有空间放导致内存溢出,也不会影响老年代。
Major GC:
回收老年代
Full GC:
Full GC是针对整个新生代、老年代、元空间的全局范围的GC
如下:新生代和老年代的比例为1:2,新生代内部又划分为三部分,比例为8:1:1,这个分配的比例是为了空间的挪动。假设新生代内存为10M的话,当第一次进入内存的大小为10M的话,存放在Eden里面,当再次进入2M的数据时,由于内存以及满了,这时GC就会分析回收之前的那10M数据,(这个GC被称为MinorGC),假如分析其中9M为垃圾,1M为可用,则将其移动到from空间中,则Eden空间就被清空了,就可以把2M的数据放进Eden空间中去,比如经过多次MinorGC后,之前保留下来的1M数据仍然存在,则把他挪动到to空间,最终挪动到老年代空间中去。
jconsole工具包
在jdk的bin目录里,双击打开,启动一个tomcat,然后在jconsole页面的菜单栏里点击连接,选择tomcat对应的那个端口。
右下角的图为:堆(左边)和栈(右边)
内存溢出与内存泄漏
形同与不同:
内存泄漏:
如下图,入栈的时候给elements数组设置值,出栈的时候从elements获取值,但是如果不将数组对应的值设置为null,则它会一直在数组中存放,这样就会造成内存泄漏。
如ArrayList的方法