02-JVM内存结构(程序计数器,jvm栈,本地方法栈,堆,方法区)

1. 程序计数器(Program Counter Register 程序计数器寄存器)

  • 作用:是记住下一条jvm指令的执行地址
  • 特点:
    • 1.每个线程都有一个自己的程序计数器(线程私有)
    • 2.不会存在内存溢出

2. 虚拟机栈(Java Virtual Machine Stacks java虚拟机栈)

2.1 定义

  • 栈-线程运行时需要的内存空间,栈由多个栈帧组成
  • 栈帧(Frame)-每个方法运行时需要的内存
  • 每个线程只能有一格活动栈帧,对应当前正在执行的那个方法
    在这里插入图片描述

2.2 问题辨析

  • 1.垃圾回收是否设计栈内存?
    答:不涉及,因为栈每次执行方法时会自动将相应的栈帧弹出栈,实现自动回收。
  • 2.栈内存分配越大越好吗?
    答:不是,假设物理内存有500m,一个线程占1m,理论上可以同时运行500个线程,而当增大栈内存为2m时,理论最多只能同时运行250个线程,结果是效率没有增加而线程的数目会减少。
  • 3.方法内的局部变量是否线程安全?
    答:如果方法内局部变量没有逃离方法的作用范围就是线程安全的,否则会存在线程安全问题。(例如:方法中return了该局部变量)

2.3 栈内存溢出(java.lang.StackOverflowError)

    1. 栈帧过多 (递归调用)
    1. 栈帧过大 超过了栈内存(不太容易出现)
    1. 第三方的库 (无限递归)例如:
class Emp{ // 员工类
	private String name;
	private Dept dept;
	... get ... set
}
class Dept{ // 部门类 
	private String name;
	private List<Emp> emps;
	... get ... set
}
public static void main(String[] args){
	Dept d = new Dept();
	d.setName("Market");
	Emp e1 = new Emp();
	e1.setName("zhang");
	e1.setDept(d);
	Emp e2 = new Emp();
	e2.setName("li");
	e2.setDept(d);
	
	d.setEmps(Arrays.asList(e1,e2));
	// 部门类转换成Json类型。
	ObjectMapper mapper = new ObjectMapper();
	System.out.println(mapper.writeValueAsString(d));
}

错误提示:无限递归 栈溢出
JsonMappingExcetion:Infinite recursion(StackOverflowError)

解决:加一个@JsonIgnore 阻止将Dept转成json类型

class Emp{ // 员工类
	private String name;
	@JsonIgnore
	private Dept dept;
	... get ... set
}

2.4 线程诊断

案例1:cpu占用过多

例如:while(true)
通过定位找到(Linux指令):

  • 用top定位哪个进程对cpu的占用过高
  • ps H -er pid,tid,%cpu | grep 进程id(用ps命令进一步定位是哪个线程)
  • jstack 进程id (可以根据线程id找到有问题的线程,进一步定位到问题代码的源码行数

案例2:程序运行时间很长

例如:死锁
同上定位

3. 本地方法栈(Native Method Stack)

Native方法: 不是由java代码编写的代码 (c ,c++)
本地方法栈:为本地方法提供内存空间

4. 堆(Heap)

4.1 定义

  • 通过new 关键字,创建对象都会使用堆内存

特点

  • 它是线程共享的,堆中对象都需要考虑线程安全的问题
  • 有垃圾回收机制

4.2 堆内存移出(java.lang.OutOfMemoryError)

4.2.1 jps工具 (jdk自带)

查看当前系统中有哪些java进程

4.2.2 jmap工具 (jdk自带)

查看堆内存占用情况 jmap -heap 进程id (jdk高版本 jhsdb jmap --heap --pid 进程id

4.2.3 jconsole工具 (jdk自带)

图形界面的,多功能的监测工具,可以连续监测

public static void main(String[] args) throws InterruptedException {
        System.out.println("1..");
        Thread.sleep(20000);
        byte[] array = new byte[1024 *1024 * 20];
        System.out.println("2....");
        Thread.sleep(20000);
        array = null;
        System.gc();// 回收
        System.out.println("3..");
        Thread.sleep(1123123);
    }

4.2.4 jvisualvm工具 (需要下载插件)

更加详细的界面更好的调优

有些垃圾回收后,内存占用依然很高

5. 方法区

它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等。
在这里插入图片描述

5.1 方法区内存溢出

  • 1.6 方法区在永久代里(java.lang.OutOfMemoryError:PermGen.space)

  • 1.8 方法区在元空间里(java.lang.OutOfMemoryError:Metaspace)

5.2 常量池

  • 常量池,就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息
  • 运行时常量池,常量池是*.class文件中的,当该类被加载,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址
  • 在这里插入图片描述
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值