JVM---内存结构

筅森魡賤 提交于 2019-12-16 00:23:47

程序计数器

Program Counter Register

  • 是记住下一条JVM指令的执行地址
  • 特点
    是线程私有的
    不会存在内存溢出

虚拟机栈

每个线程运行所需要的内存,成为虚拟机栈
每一个栈是由多个栈帧组成,栈帧是每个方法运行时需要的内存,一个栈帧对应着一个方法调用
每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法

垃圾回收是否涉及栈内存?
不会,栈内存是一次次的方法调用产生的栈帧内存,而栈帧内存在方法调用完成后,会自动出栈。
栈内存分配越大越好吗?
通过参数-Xss size参数来指定栈的大小
栈内存如果分配的大,则可以执行的线程数,越来越少,因为物理内存是一定的。
方法内的局部变量是否是线程安全?
局部变量是线程私有的,所以是线程安全的
如果变量是多个共享的,则需要考虑线程安全问题
总结:如果方法内的局部变量没有逃离方法的作用范围,它是线程安全的
如果局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全
(引用作为参数,作为返回值)

/**
 * 局部变量的线程安全问题
 */
public class Demo1_17 {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();
        sb.append(4);
        sb.append(5);
        sb.append(6);
        new Thread(()->{
            m2(sb);
        }).start();
    }

    public static void m1() { //线程安全的
        StringBuilder sb = new StringBuilder();
        sb.append(1);
        sb.append(2);
        sb.append(3);
        System.out.println(sb.toString());
    }

//不是线程安全的,StringBuilder作为参数,main线程在修改它,新的线程也在修改,属于线程共享的
    public static void m2(StringBuilder sb) { 
        sb.append(1);
        sb.append(2);
        sb.append(3);
        System.out.println(sb.toString());
    }

//不是线程安全的,当成结果返回,其他线程可以修改它的值
    public static StringBuilder m3() { 
        StringBuilder sb = new StringBuilder();
        sb.append(1);
        sb.append(2);
        sb.append(3);
        return sb;
    }
}

栈内存溢(java.lang.StackOverFlowError)

  • 栈帧过多导致栈内存溢出(方法的递归调用)
  • 栈帧过大导致栈内存溢出(出现情况较少)
  • 第三方库的使用也会导致栈内存溢出(json解析)

线程运行诊断

案例1:cpu占用过多

  • linux中用top定位那个进程对cpu的占用过高
  • ps H -eo pid,tid, %cpu | grep 进程id(ps命令进一步定位是哪个线程引起的cpu占用过高)
  • 使用JDK自带的jstack工具,使用的格式:jstack 进程id,可以根据线程id找到有问题的线程,进一步定位到问题代码的源码行号

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

本地方法栈

给本地方法运行提供内存空间,也是线程私有的

  • 通过new关键字,创建对象都会使用堆内存
  • 它是线程共享的,堆中对象需要考虑线程安全问题
  • 有垃圾回收机制

堆内存溢出问题(java.lang.OutOfMemoryError)

调整堆空间的最大值:-Xmx
排查堆内存问题时,将堆空间调小些

堆内存诊断

jps工具:查看当前系统中有哪些java进程,显示进程编号
jmap工具:查看堆内存占用情况,只能查询某一个时刻进程堆内存占用情况,jmap -head 进程id
jconsole工具:图形界面的,多功能的监测工具(线程,cpu),可以连续监测
jvisualvm工具:图形界面的

方法区

是线程共享的,存储了和类的结构相关的信息,方法区在虚拟机启动时创建,方法区是一个规范,可有不同的实现,JDK1.8之前方法区采用永久代实现,JDK1.8方法区采用元空间来实现

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!