jvm内存区域分配
java 堆是各线程共享的内存区域,在jvm启动时创建,这块区域是jvm中最大的
用于存储应用的对象和数组,也是gc的主要回收区,一个jvm实例只存在一个队内存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类,方法,常变量放到对内存中,以方便执行器执行。堆内存分为三部分,新生代,老年代,永久代。
Jdk1.6及之前:常量池分配在永久代 。
Jdk1.7:有,但已经逐步“去永久代” 。
Jdk1.8及之后:无永久代,改用元空间代替(java.lang.OutOfMemoryError: PermGen space,这种错误将不会出现在JDK1.8中)。
jvm内存结构: 堆,栈,方法区,jvm内存结构
java内存模型: java内存结构 JMM
栈: 线程栈
java栈是线程私有的是在线程创建的时候创建,他的生命周期是跟随线程的生命期,线程结束栈内存也就释放,对于栈来说不存在垃圾回收问题,只有线程一结束就over,生命周期和线程一致。基本类型的变量和对象的引用变量都是在函数的栈内存中分配 。
每个方法执行的时候都会有一个栈帧栈帧里主要存储3类数据:
局部变量表: 输入参数和输出参数以及方法内的变量
栈操作: 记录出栈和入栈的操作
栈帧数据: 包括类文件,方法等。
java内存结构;
堆: 存放new ,数组,堆线程共享
线程安全问题: JMM主内存
虚拟机栈(线程栈):栈不共享。每个线程都有自己独立的栈空间,相互之间不影响。JMM本地空间/工作内存/主内存的副本数据。
本地方法栈: java代码调用c语言代码实现jni
方法区: 存放: 1,类的信息, 2 常量 ,3 静态变量 4 运行时常量。多个线程共享,存在安全问题:
程序计数器(pc程序计数器): 记录当前线程行号,在多线程的情况下记录多线程之间上下文切换的位置。
类加载器: 读取我们的class字节码文件到jvm 虚拟机内存中。
栈:
栈: 是一种数据结构 先进后出 后进先出
队列: 先进先出
虚拟机栈存放:
1, 局部变量
2,栈帧 (记录方法的信息)
3,栈操作。
栈空间什么时候销毁:1 当我们的方法执行完毕后,栈帧空间会被销毁。
2,抛出异常栈帧空间会销毁
package com.taotao.jvm1.day04;
/**
*@author tom
*Date 2020/8/12 0012 8:33
*栈帧空间
*/
public class Test005 {
public int mk01() {
//一个方法一个栈帧空间
return mk02();
}
private int mk02() {
int j = 1;
int z = j + 1;
return j;
}
/**
* 3个栈帧
* @param args
*/
public static void main(String[] args) {
Test005 a = new Test005();
Test005 b = a;
System.out.println(a.mk01());
new Thread().start();
}
}
程序计数器:
1,记录我们当前的线程执行的行号,只有在多线程里才有作用,线程切换的时候通过程序计数器知道在哪继续执行。
多线程上下文切换的概念:
将class文件 反汇编:
javap -c -v class地址
Javap -c -v F:\works\2020\mayiketang\7\jvm\jvm1\target\classes\com\taotao\jvm1\classloader\Test005.class
本地方法栈
本地方法栈和JVM栈发挥的作用非常相似,也是线程私有的,区别是JVM栈为JVM执行Java方法(也就是字节码)服务,而本地方法栈为JVM使用到的Native方法服务。它的具体做法是在本地方法栈中登记native方法,在执行引擎执行时加载Native Liberies.有的虚拟机(比如Sun Hotpot)直接把两者合二为一。
也就是 java调用c语言代码 jni技术
栈帧空间包括: 局部变量表 操作数栈 动态链接 方法地址
栈帧:
一个方法对应一个栈帧空间。每个方法都有独立的栈帧内存空间,栈数据结构: 先进后出销毁。
栈帧内部细节结构: 局部变量表,操作数栈,动态链接,方法出口。,栈帧就是每个方法需要执行的运行时内存空间。
每个运行时需要的内存的叫做虚拟机栈
每个栈有多个栈帧组成,对应着每次方法调用时占用的内存
每个线程只能有一个活动的栈。对应着当前执行的方法。
idea 查看栈帧:
image.png
程序计数器:
记录当期线程执行下一行指令的执行地址,作用主要记录多线程上下文切换过程中记录当前线程的下一指令。
本地方法栈:
本地方法栈和jvm栈发挥的作用非常类似,也是线程私有的,区别是jvm栈执行java方法(也就是字节码服务),而本地方法栈为jvm使用到的native方法服务,特的具体做法是本地方法栈中登记native方法。在执行引擎时加载nativeLIberies。也就是java调用c语言代码jni技术
栈溢出:
递归调用: 栈溢出
原因: 在我们占空间中产生了非常多的栈帧空间一直没有释放。
如何避免栈溢出:
1,增加栈内存
2,减少递归深度调用。
package com.taotao.jvm1.day04;
/**
*@author tom
*Date 2020/8/12 0012 10:06
*-Xss256k java.lang.StackOverflowError 性能调优
*/
public class StackTest {
private static int count;
public static void main(String[] args) {
mk01();
}
private static void mk01() {
count++;
System.out.println("count:"+count);
mk01();
}
}
image.png
image.png
堆内存溢出:
在申请内存的时候,内存不足,产生对内存溢出:
package com.taotao.jvm1.day04;
import java.util.ArrayList;
/**
*@author tom
*Date 2020/8/13 0013 9:36
*-Xmx8m
*/
public class HeapTest {
public static void main(String[] args) {
int i=0;
try {
ArrayList<String> strings=new ArrayList<>();
while (true){
strings.add("mk");
i++;
}
}catch (Exception e){
e.printStackTrace();
}
}
}
堆内存泄露:
java.lang.OutOfMemoryError: GC overhead limit exceeded
这种机制也会有一些问题:就是被占用的内存,经过多次长时间的gc操作都无法回收,导致内存越来越少,俗称内存泄露。jvm就会报java.lang.OutOfMemoryError: GC overhead limit exceeded错误。
package com.taotao.jvm1.day04;
import java.util.HashMap;
/**
*@author tom
*Date 2020/8/13 0013 9:41
*内存泄露问题
* -Xmx3M -Xms3M
*/
public class HashMapMemoryLeak {
public static void main(String[] args) {
HashMap<HashKey2, Integer> map = new HashMap<HashKey2, Integer>(1000);
int counter = 0;
while (true) {
//循环插入新对象new出很多很多内存地址不等的对象
HashKey2 p = new HashKey2("mk", "22");
map.put(p, 1);
counter++;
if (counter % 1000 == 0) {
System.out.println("map size:" + map.size());
System.out.println("运行:" + counter + "次后,可用内存剩余" + Runtime.getRuntime().freeMemory() / (1024 * 1024) + "MB");
}
}
}
static class HashKey2 {
private final String id;
private String name;
public HashKey2(String name, String id) {
this.name = name;
this.id = id;
}
public void setName(String name) {
this.name = name;
}
@Override
public int hashCode() {
return id.hashCode();
}
/**
* hashMap 不管怎么new 多少次 只会key 只会引入一次 不会继续添加。
*
* @param obj
* @return
*/
@Override
public boolean equals(Object obj) {
if (obj instanceof HashKey2)
return name.equals(((HashKey2) obj).name) && id.equals(((HashKey2) obj).id);
else
return false;
}
}
}
堆内存诊断工具
A. Jps工具 - 查看当前系统有那些java进程
1.Jps
2.jmap -heap 进程id
Jmp工具 查看堆内存占用情况
B.Jconsole工具 图形界面管理 线程、cpu 堆内存
E:\java8\jdk\bin\jconsole.exe
网友评论