jvm内存结构-程序计数器、虚拟机栈、本地方法栈、堆、方法区、直接内存

程序计数器

1.1 定义

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

作用,是记住下一条jvm指令的执行地址

jvm指令:jvm跨平台的基础就是jvm指令,对所有平台(win,mac,linux)一致

指令地址:根据地址,查找命令

执行过程:程序计数器中获取地址,根据地址获取指令,拿到一条指令交给解释器,解释器解释成机器码,机器码交给cpu执行,程序计数

器存入下一个地址...(重复)

机器码

解释器将每条执行(例如getstatic)解释成机器码

cpu只认机器码

寄存器

1)通过寄存器物理实现程序计数器

2)程序计数器是Java对物理硬件的屏蔽和抽象

3)是CPU里读取速度最快的单元,读写指令地址非常频繁,所以jvm在设计时

就将cpu中寄存器当做程序计数器

特点

是线程私有的

cpu调度器组件

多线程执行时,分配cpu时间片

时间片

如果时间片内线程1未执行完,线程1状态会暂存

切换到线程2

线程2时间片用完,切换到线程1,执行剩余代码

线程切换

程序计数器记录下一条执行指令

假设线程1执行到9,程序计数器记录10

每个线程都有自己程序计数器

不会存在内存溢出

jvm规范中唯一不会出现内存溢出的区

虚拟机栈

Java Virtual Machine Stacks Java 虚拟机栈)

每个线程运行时所需要的内存,称为虚拟机栈  多个线程会有多个

每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存  一个栈帧对应一次方法调用

每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法  栈顶栈帧

/**

 * 动态演示虚拟机栈 debug模式运行

 */

public class JvmStacks {

    public static void main(String[] args) {

        method1();

    }

    public static void method1(){

        method2(1,2);

    }

    public static int method2(int a,int b){

        int c=a+b;

        return c;

    }

}

虚拟机栈与栈帧关系

单个栈帧

线程调用一个方法,开辟栈帧,压栈,

执行结束,栈帧出栈,释放栈帧占用内存

多个栈帧

调用方法1,开辟栈帧,

方法1调用方法2,再次开辟栈帧压栈

方法2执行完,释放方法2栈帧(多个调用亦如此

  1. 垃圾回收是否涉及栈内存?

不需要

每次方法调用,创建栈帧,执行完后便销毁

gc回收的是堆

  1. 栈内存分配越大越好吗?

不是

大了,减少线程数(物理内存固定,假设500M

,栈1M,最多可以500线程,栈5M 最多100线

程)

增加递归次数

代码运行时通过虚拟机参数指定栈内

-Xss size

linux/mac/oracle solaris默认1M

win取决虚拟内存

(默认堆栈大小取决于PE(Portable Executable)文件(java.

exe)。如果运行32位Java应用程序,

则默认堆栈大小为320K。如果运行64

位Java应用程序,则默认堆栈大小为

1024K。)

一般采用默认

设置

run->edit configuration->vm options

3. 方法内的局部变量是否线程安全?

如果方法内局部变量没有逃离方法的作用访问,它是线程安全的

如果是局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全

基本类型逃离,线程安全(在栈里

 

public class ThreadSafe {

    static int x;

    //多线程调用此方法线程安全

    static void m1(){

        int x=0;

        for (int i = 0; i <5000 ; i++) {

            x++;

        }

        System.out.println(x);

    }

    //多线程调用此方法线程不安全

    static void m2(){

        for (int i = 0; i <5000 ; i++) {

            x++;

        }

        System.out.println(x);

    }

}

public class BThreadSafe2 {

    public static void main(String[] args) {

        StringBuilder sb = new StringBuilder();

        sb.append(1);

        sb.append(3);

        sb.append(4);

        new Thread(()->{f2(sb)}).start();

    }

    public static void f1(){//线程安全

        StringBuilder sb = new StringBuilder();

        sb.append(1);

        sb.append(3);

        sb.append(4);

        System.out.println(sb.toString());

    }

//线程不安全,其他线程可能访问到参数

//StringBuffer线程安全

    public static void f2(StringBuilder sb){

        sb.append(1);

        sb.append(3);

        sb.append(5);

        System.out.println(sb.toString());

    }

    //线程不安全 逃离作用范围

    public static StringBuilder f3(){

        StringBuilder sb = new StringBuilder();

        sb.append(1);

        sb.append(3);

        sb.append(4);

        return sb;

    }

}

栈内存溢出

栈帧过多导致栈内存溢出

public class CStackOverFolwer {

    private static int count;

    public static void main(String[] args) {

        try {

            f();

        } catch (Throwable e) {

            e.printStackTrace();

            System.out.println(count);

        }

    }

    public static void f() {

        count++;

        f();

    }

}

三方代码导致栈溢出

栈帧过大导致栈内存溢出(不太好演示)

线程运行诊断

案例1: cpu 占用过多

定位

运行:

nohup java XXX &

top定位哪个进程对cpu的占用过高

ps H -eo pid,tid,%cpu | grep 进程id (用ps命令进一步定位是哪个线程引起的cpu占用过高)

jstack 进程id    

jdk提供

输出jvm及用户线程

线程id为16进制

可以根据线程id 找到有问题的线程,进一步定位到问题代码的源码行号

H 展示所有进程,所有线程

-eo 输出指定内容 后面指定内容

pid 进程id

tid 线程id

%cpu cpu占用

案例2:程序运行很长时间没有结果

方法同上,死锁导致

本地方法栈

本地方法栈:

jvm调用本地方法时给本地方法提供的内存空间

本地方法

1)指非Java代码编写的方法

2)Java代码有局限性,不能直接操作底层操作系统,需要使用c/c++编写的本地方法直接操作底层操作系统api

4)基础类库 执行引擎会调用,数量很多

native方法举例

1) object类中clone() hashCode() notify() notifyAll() wait(ling timeout)是native

2) 该类方法无Java代码实现,是用c/c++编写

3) 是通过native接口间接调用c/c++

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java邦邦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值