day1_内存区域

news2024/11/16 17:36:54

文章目录

  • 1 程序计数器
  • 2 虚拟机栈(JVM 栈)
    • 2.1 基本概念以及演示
    • 2.2 栈内存溢出的情况
    • 2. 3 线程排查
  • 3 本地方法栈
  • 4 堆
    • 4.1 堆内存溢出以及诊断
  • 5 方法区

JVM的内存区域,主要分为了5个部分: 方法区, 堆, 程序计数器, 虚拟机栈,本地方法栈。其中程序计数器,虚拟机栈和本地方法栈是线程私有的

1 程序计数器

程序计数器中存放的是下一条执行指令的地址。程序计数器的特点是:

  • 线程私有
  • 不会出现内存溢出的情况(而栈,堆则会出现内存溢出)

2 虚拟机栈(JVM 栈)

2.1 基本概念以及演示

  • 虚拟机栈:每个线程所需要的内存。而这个栈内存,我们可以通过参数-Xss来设置的,在Linux中默认好像是1MB,这个值可以在IDEA中设置,如下所示:
    在这里插入图片描述
    但是并不是说分配到的栈内存越大约好的,因为物理内存是固定的,那么如果栈内存越大,那么导致线程数就变少了
  • 栈帧:每次方法被调用时所需要分配的内存(需要给这个方法的参数,局部变量,返回值分配内存)。当这个方法执行完毕之后,就会将这个栈帧从栈内存中跳出,自动释放这个栈帧占用的内存
  • 活动栈帧: 当前线程正在执行的方法(这个方法位于栈顶)

如下面的代码:

public class Demo1 {
    public static void main(String[] args) {
         method1();   
    }

    private static void method1() {
        method2(2, 3);
    }

    private static int method2(int a, int b) {
        int c = a + b;
        return c;
    }
}

给method1方法的地方打一个端点,此时进入调试模式,就可以看到对应的栈的情况了:
在这里插入图片描述
当我们进入到了method1方法内部,在进入method2方法内部,此时栈中的情况就有了3个栈帧,如下所示:
在这里插入图片描述
此时就有了3个栈帧(method2, method1, main),并且当前线程正在执行method2方法,所以method2是一个活动栈帧(位于栈顶)
当method2执行完毕之后,method2这个栈帧就会从栈中弹出,自动挥手这部分的内存,此时栈中就剩下了method1, main这2个栈帧了,如下所示:
在这里插入图片描述
同样的,当method1执行完毕返回之后,method1这个栈帧从栈中弹出,并被回收,而栈中只剩下了main栈帧,当main方法执行完毕之后,main从栈中弹出并被回收。
所以整个过程中栈的变化流程为:
在这里插入图片描述
根据上面提到的,当这个方法使用完之后,对应的栈帧就会从栈中弹出,并且自动回收这部分的内存,所以垃圾回收并没有涉及到栈内存,也即是说不会回收栈

同时需要注意的是,每次方法被调用,都会产生一个新的栈帧,那么这时候方法内的局部变量是否存在一个线程安全的问题?
答案取决与这个局部变量是否逃离了这个方法,所谓的逃离,就是说这个局部变量(是一个引用类型)是否是从方法外部传入的变量,或者说这个局部变量作为方法的返回值返回
如果局部变量既不是由方法外部传入的,并且没有作为返回值返回,那么是线程安全的,否则如果这个局部变量是由方法外部传入,或者作为返回值返回,并且这个局部变量是一个引用类型,那么此时可能会出现线程安全问题,如下所示:

public class Demo1 {
    public static void main(String[] args) throws InterruptedException {
         StringBuilder stringBuilder = new StringBuilder();
         new Thread(() -> {
             method3(stringBuilder);
         }).start();
         new Thread(()->{
             method3(stringBuilder);
         }).start();
         stringBuilder.append(1);
         stringBuilder.append(2);
         stringBuilder.append(3);
         Thread.sleep(2000);
         System.out.println(stringBuilder.toString());
    }
    private static StringBuilder method4(){
    //局部变量作为返回值返回,并且是一个引用类型,所以这个局部变量需要考虑一下线程安全问题
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(4);
        stringBuilder.append(5);
        return stringBuilder;
    }
    private static void method3(StringBuilder stringBuilder) {
    //这个方法中的参数是从方法外部传入的,并且是是一个引用类型,所以这个局部变量需要考虑线程安全问题
        stringBuilder.append(4);
        stringBuilder.append(5);
    }
}

2.2 栈内存溢出的情况

如果出现了栈内存溢出,那么就会抛出错误StackOverFlowError。那么导致栈内存溢出的原因主要有:

  • 栈帧过多导致的栈内存溢出(这个是常见的)
    栈帧过多,也即是方法被调用的多了,并且这些方法并没有执行完,所以不会从栈中跳出,从而导致栈溢出。
    例如我们在进入递归的时候,如果这个递归结束的条件没有设置好,就会导致不断进入递归,而无法结束方法调用,此时就会出现了栈内存溢出。如下面的代码所示:
    public class Demo1 {
        static int count = 0;
        public static void main(String[] args) {
             try{
                 method1();
             }catch (Error e){
                 System.out.println(count);
                 e.printStackTrace();
             }
        }
        private static void method1() {
            ++count;
            method1();
            method2(2, 3);
        }
    }
    
    最后的结果如下所示:
    在这里插入图片描述
    另一种可能就是我们将对象以字符串形式输出这个对象信息的时候,对象之间存在循环引用的关系,从而导致StackOverFlowError,如下所示:
public class Demo1 {

    public static void main(String[] args) {
         //method1();
        Employee e1 = new Employee();
        e1.setName("a");
        Department department = new Department();
        department.setName("d_01");
        e1.setDepartment(department);
        department.setEmployeeList(Arrays.asList(e1));
        System.out.println(department);
    }
}

class Employee{
    private String name;
    private Department department;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Department getDepartment() {
        return department;
    }

    public void setDepartment(Department department) {
        this.department = department;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", department=" + department +
                '}';
    }
}

class Department{
    private String name;
    private List<Employee> employeeList;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Employee> getEmployeeList() {
        return employeeList;
    }

    public void setEmployeeList(List<Employee> employeeList) {
        this.employeeList = employeeList;
    }

    @Override
    public String toString() {
        return "Department{" +
                "name='" + name + '\'' +
                ", employeeList=" + employeeList +
                '}';
    }
}

正如上面的代码所示: Employee -> Department,而Department中的List<Employee>又依赖于Employee,此时就出现了循环以来的问题,最后打印Department的时候,就会抛出StackOverFlowError错误了。

  • 栈帧过大导致的栈内存溢出.

2. 3 线程排查

如果要排查占用内存最高的线程,我们可以使用linux中top命令进行排查,对应的步骤如下所示:

  • 切换成root身份,之后输入top -c 命令,来查看各个进程占用内存的情况,此时就可以得知占用内存最高的进程id了
  • 输入命令top -pH PID(PID是上面查到的进程ID),这样就可以得知这个进程下面的各个线程占用内存的情况,此时可以得知这个进程下面哪个线程tid占用内存最高。
  • 输入命令printf "%x\n" tid,将上一步中得到的占用内存最高的线程id以十六进制的形式打印出来
  • 输入命令jstack PID | grep xxx,来查看PID这个进程下面的各个线程的运行情况,同时利用grep xxx,来查找tid为xxx的线程,其中xxx就是上面获得的线程id的十六进制形式(第3步可以得知)

由于死锁而导致程序迟迟无法输出,那么我们同样可以根据上面的步骤,来进行排查,但是由于程序无法迟迟输出,那么可能导致占用的内存并不是很多,所以使用top命令不会很容易找到我们需要寻找的进程的进程id。
所以我们需要先利用命令ps -ef | grep "xxx.java",来寻找我们需要看的进程,从而得到他对应的进程ID。最后通过命令jstack PID来查看这个进程下面的各个线程的运行状态。
如下面一段死锁的代码:

public class DeadLock {
    static LockA lockA = new LockA();
    static LockB lockB = new LockB();
    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (lockA){
                System.out.println("ThreadA got lockA");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lockB){
                    System.out.println("ThreadA got lockB");
                }
            }
        }).start();

        new Thread(() ->{
            synchronized (lockB){
                System.out.println("ThreadB got lockB");
                synchronized (lockA){
                    System.out.println("ThreadB got lockA");
                }
            }
        }).start();
    }
    static class LockA{
    }

    static class LockB{

    }
}

当我们使用linux进行排查的时候,首先利用ps -ef | grep "DeadLock",如下所示:
在这里插入图片描述
然后我们利用命令jstack 6536查看这个进程下面的各个线程的运行状况:
在这里插入图片描述

3 本地方法栈

4 堆

堆是通过new创建出来的对象,存放到堆中的。所以堆拥有的特点是:

  • 线程共享的。和前面3者不同,堆它是线程共享的,所以需要考虑到线程安全的问题
  • 有垃圾回收机制。当一个对象不在被使用的时候,那么就会被回收。而虚拟机栈则没有垃圾回收机制,因为每当方法执行完毕之后,对应的栈帧就会从栈中弹出,并且会自动回收这个栈帧对应的内存。

4.1 堆内存溢出以及诊断

尽管堆中存在垃圾回收机制,但是如果线程中的某一个list一直在使用,并且list中的元素数量不断增加,那么当元素的数量达到某一个数字的时候,就会出现堆内存溢出,发生了OutOfMemoryError: Java heap space,如下面的代码所示:

public class Demo3 {
    public static void main(String[] args) {
        int i = 0;
        try{
            List<String> list = new ArrayList<>();
            String a = "hello";
            while(true){
                list.add(a);
                a = a + a;
                ++i;
            }
        }catch(Error e){
            System.out.println(i);
            e.printStackTrace();
        }
    }
}

尽管我们可以通过-Xmx参数来设置堆内存的大小,在短暂解决堆内存溢出的问题,但这是治标不治本的方法,所以我们需要进行堆内存诊断,常见的手段主要有:

  • jps : 通过这个命令,可以查看当前系统的java进程,同时获得对应的进程ID
  • jmap: 可以通过命令jmap -head pid来查看这个进程的堆内存情况。所以在使用这条命令之前,需要获取对应的进程id,所以需要使用jps来获取进程id.
  • jconsole: 是一个可视化工具,可以很清楚看到堆内存的运行情况
  • jvisualvm: 同样是一个可视化工具,但是比jconsole更加完善,因为可以dump,存储某一个时刻下面的堆内存使用的情况,然后进行分析。

5 方法区

方法区的特点:

  • 线程共享
  • 在虚拟机启动的时候就创建了的
  • 存储的信息主要有: class(类的信息,例如属性,方法等)、classloader(类加载器)、常量池

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/487155.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

解读Toolformer

【引子】读论文Toolformer: Language Models Can Teach Themselves to Use Tools&#xff0c;https://arxiv.org/pdf/2302.04761.pdf&#xff0c;再阅读了几篇关于Toolformer的网络热文&#xff0c;于是“无知者无畏”&#xff0c;开始自不量力地试图解读Toolformer。 大语言模…

《数据安全法》

中华人民共和国数据安全法 &#xff08;2021年6月10日第十三届全国人民代表大会常务委员会第二十九次会议通过&#xff09; 目录 第一章 总则 第二章 数据安全与发展 第三章 数据安全制度 第四章 数据安全保护义务 第五章 政务数据安全与开放 第六章 法律责任 第…

Gradio学习笔记--Gradio基本用法和简单案例

​​​​​​​ 目录 1--配置Gradio 2--Hello World案例 2-1--基本使用 2-2--进阶使用 3--图像案例 3--常用类 Interface 和 Blocks 1--配置Gradio 使用 pip 安装 Gradio&#xff1a; pip install gradio 2--Hello World案例 2-1--基本使用 import gradio as grdef …

代码随想录算法训练营第二十八天 | 三个逗点四个子串的类型题、子集题要取所有节点、子集与树层去重

93.复原IP地址 文档讲解&#xff1a;代码随想录 (programmercarl.com) 视频讲解&#xff1a;回溯算法如何分割字符串并判断是合法IP&#xff1f;| LeetCode&#xff1a;93.复原IP地址_哔哩哔哩_bilibili 状态&#xff1a;不会做。这是三个逗点四个子串的类型题。 思路 这是切割…

重新定义分析 - EventBridge 实时事件分析平台发布

对于日志分析大家可能并不陌生&#xff0c;在分布式计算、大数据处理和 Spark 等开源分析框架的支持下&#xff0c;每天可以对潜在的数百万日志进行分析。 事件分析则和日志分析是两个完全不同的领域&#xff0c;事件分析对实时性的要求更高&#xff0c;需要磨平事件领域中从半…

【STM32】基础知识 第十一课 时钟

【STM32】基础知识 第十一课 时钟 概述时钟系统的基本概念什么是时钟时钟树锁相环APB 时钟树F1 系列时钟树F4 系列时钟树F7 系列时钟树H7 系列时钟树 系统时钟配置系统时钟配置步骤外设时钟的使能和使能HAL_RCC_OscConfig() 函数HAL_RCC_ClockConfig() 函数 概述 STM32 微控制…

超级香,分享8个相见恨晚的Python库

看到新奇又实用的第三方库&#xff0c;当然要分享出来咯~ Plottable Plottable是一个Python库&#xff0c;用于在matplotlib基础上绘制精美表格。例如下图所示表格。 代码如下&#xff1a; import matplotlib.pyplot as plt import numpy as np import pandas as pd from ma…

Leangoo领歌轻量级协作-OKR目标管理

​本场景是OKR目标管理模板&#xff0c;用Leangoo管理和跟踪OKR可以提升OKR的透明度和传递的即时性&#xff0c;驱动团队的积极性、促进共享和协作、提升沟通和协作的效率&#xff0c;帮助企业快速落地OKR。 OKR&#xff08;Objectives and Key Results目标与关键结果&#xf…

Packet Tracer - 谁会听到广播?

Packet Tracer - 谁会听到广播&#xff1f; 目标 第 1 部分&#xff1a;观察 VLAN 实施中的广播流量 第 2 部分&#xff1a;完成思考题 拓扑图 场景 在本练习中&#xff0c;将完全填充一个配有 24 个端口的 Catalyst 2960 交换机。 所有端口都已使用。 您将观察 VLAN 实施…

PS磨皮插件portraiture最新版磨皮工具

Portraiture是一款智能磨皮插件&#xff0c;为Photoshop和Lightroom添加一键磨皮美化功能&#xff0c;快速对照片中皮肤、头发、眉毛等部位进行美化&#xff0c;无需手动调整&#xff0c;大大提高P图效率。全新4版本&#xff0c;升级AI算法&#xff0c;并独家支持多人及全身模式…

剪纸中秋海报

打开【Ps】&#xff0c;新建【A4画布】&#xff0c;双击背景图层【解锁】&#xff0c;再添加【图案叠加】图层样式&#xff0c;选择最后一个图案&#xff0c;增加【缩放】。 【椭圆选框工具】按住【Shift】键画一个正圆&#xff0c;填充颜色#0e8993&#xff0c;添加【渐变叠加】…

假期做了一项调研:大厂为何都要自研RPC框架?结果合乎情理

大家好&#xff0c;我是冰河~~ 五一假期过的可真快&#xff0c;今天开始&#xff0c;又要搬砖了。在五一假期当中&#xff0c;冰河做了一项调研&#xff0c;感觉结果还是挺合乎情理的。 翻看招聘信息 先来看我在某招聘网站上随便搜索了下Java招聘的岗位&#xff0c;看到的招…

做好防雷检测的重要意义和作用

防雷检测是一项非常重要的工作&#xff0c;它可以保障人们的生命财产安全&#xff0c;并维护国家的安全稳定。地凯科技将从防雷的重要性、防雷检测的行业应用和防雷行业国标三个方面来阐述防雷检测的重要性。 一、防雷的重要性 随着科技的不断发展&#xff0c;人们的生活和工作…

unity中实现经典的2d横版单向跳跃平台

经常玩2d横版游戏的朋友们相信对这样的效果一定对这种单向跳跃平台很熟悉&#xff1a;我希望我的角色可以通过跳跃跳上平台&#xff0c;然后在平台之上按下键盘的下键后从平台上落下。 那么想要实现这样的效果具体要怎么做呢&#xff1f;我们还是先将想要实现的效果进行一个逻…

微服务分布式搜索引擎 ElasticSearch 搜索结果处理 排序、分页与高亮

文章目录 ⛄引言一、排序⛅普通字段排序⚡地理坐标排序 二、分页⌚基本分页⏰深度分页 三、高亮⚡实现高亮 ⛵小结 ⛄引言 本文参考黑马 分布式Elastic search Elasticsearch是一款非常强大的开源搜索引擎&#xff0c;具备非常多强大功能&#xff0c;可以帮助我们从海量数据中…

python 实现模糊聚类

python模糊聚类细节与实现 前言数学逻辑代码框架Python实现数据预处理 preProcess获得相似矩阵 getSimilarityMatrix获取传递闭包 transitiveClosure模糊运算 fuzzMatrixProduct实现聚类 mergeProcess 实例演示 ![在这里插入图片描述](https://img-blog.csdnimg.cn/b062c59c282…

【Redis】持久化机制详解:从RDB到AOF,你需要知道的一切

本文目录 RDB&#xff08;默认&#xff09; 自动触发 &#x1f19a; 手动触发 优点 缺点 何时会触发RDB快照 AOF 启用 AOF 配置路径 AOF 文件&#x1f4c3; AOF 的写回策略 AOF 的重写机制 优点 缺点 RDB & AOF 优先级 终极方案&#xff1a;RDB AOF 混合方…

城市轨道交通自动售检票系统

概述 城市轨道交通自动售检票系统&#xff08;AFC&#xff09;是基于计算机、通信、网络、自动控制等技术&#xff0c;实现轨道交通售票、检票、计费、收费、统计、清分、管理等全过程的自动化系统。可以提高轨道交通的运营效率&#xff0c;满足乘客的快速出行需求&#xff0c…

看完张一鸣近十年微博,我总结了这些成长特质

从程序员到 CEO 关注我的朋友&#xff0c;很多都是技术背景&#xff0c;之前在一个知乎回答里提到过&#xff0c;技术人在创业过程中并不占优势。 编程和创业是两个不同的体系&#xff0c;大部分技术工程师在工作中&#xff0c;解决的往往是一个限定的问题&#xff0c;而创业是…

【分享】ChatGPT的key,用key免费使用ChatGPT(每天更新)

1、ChatGPT用法总结&#xff1a; 自动化文本生成&#xff1a;可以用GPT生成文章、新闻、文本摘要&#xff0c;甚至小说、诗歌等文学作品。语音生成&#xff1a;结合语音合成技术&#xff0c;GPT可以生成自然流畅的语音&#xff0c;可以用于语音助手、交互式语音应用等。问答系统…