首页 > 系统相关 >介绍下Java内存区域(运行时数据区)

介绍下Java内存区域(运行时数据区)

时间:2022-08-21 20:01:32浏览次数:46  
标签:Java 虚拟机 区域 线程 内存 字符串 方法

介绍下Java内存区域(运行时数据区)

Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分成若干个不同的数据区域。JDK 1.8 和之前的版本略有不同。

下图是 JDK 1.8 对JVM做的改动,把方法区的具体实现----元空间已到了本地内存中。

各线程共享的:堆、方法区(元空间)、直接内存;

各线程私有的:程序计数器、虚拟机栈、本地方法栈;

1️⃣ 程序计数器

它是个什么?

程序计数器是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。字节码解释器工作时通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等功能都需要依赖这个计数器来完成。

它有什么用?

1、字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制;

2、在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了;

补充:

  • 每个线程内都有一个程序计数器,因为每个线程各自执行的指令地址是不一样的。当多线程并发执行时,CPU中的线程调度器会给每个线程分配一个时间片,当某个线程在时间片内仍没执行完,那程序计数器就得记录当前线程下一次被分配时间片执行时的下一条jvm指令的执行地址。
有什么需要注意的点?
  • 线程私有;
  • 不会存在内存溢出(⚠ 程序计数器是JVM中唯一一个不会出现 OutOfMemoryError 的内存区域)
  • 它的生命周期随着线程的创建而创建,随着线程的结束而死亡;
  • Java中把CPU中的寄存器当作了“程序计数器”,因为寄存器是CPU中读取速度最快的单元;

2️⃣ 虚拟机栈

它是个什么?

每个线程运行时所需要的内存,称为“虚拟机栈”。所有的 Java 方法调用都是通过栈来实现的(也需要和其他运行时数据区域比如程序计数器配合)。

每一个线程运行时需要给每一个线程划分一块内存空间,那虚拟机栈就是每一个线程运行时所需要的内存空间。一个线程就有一个虚拟机栈,多个线程就有多个栈。

方法调用的数据需要通过栈进行传递,每一次方法调用都会有一个对应的栈帧被压入栈中,每一个方法调用结束后,都会有一个栈帧被弹出。

每个栈由一个个栈帧(Frame)组成,对应着每次方法调用时所占用的内存。每个栈帧内部都拥有:局部变量表、操作数栈、动态链接、方法返回地址。栈顶部的,即正在被执行的方法,叫活动栈帧。

它有什么用?

栈用来执行所有方法,并返回结果。

什么情况下会栈内存溢出?
  • 栈帧过多导致栈内存溢出。如递归调用,没有递归出口,当线程请求栈的深度超过当前 Java 虚拟机栈的最大深度的时候,就抛出 StackOverFlowError 错误;
  • 栈帧过大导致栈内存溢出。如果虚拟机在动态扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常;
有什么需要注意的点?
  1. 一个方法被调用时所占用的内存就是一个栈帧,方法中有方法参数,局部变量,返回地址等这些信息都是要占用内存的。所以每个方法执行时,需要预先把内存分配好。
有没有试过线程诊断?(Linux环境下)
# 可以实时监测后台进程对CPU和内存的占用情况,top命令可以得到进程编号,但不能定位这个进程中哪个线程有问题
top	

# ps命令进一步可以查看该进程下,所有线程对CPU的占用情况,看是哪一个线程占用过高(注意:输出的线程编号是十进制的)
ps H -eo pid,tid,%cpu | grep 进程id	

# jstack命令可以得到该进程下的所有线程运行信息(注意:输出的线程中有个参数是nid,值就是线程编号,16进制的,把上面10进制换算成16进制)
jstack 进程id 	# 而且,可以定位到Java哪个类中第几行代码有问题

上面的三步法,对于常见的死循环、死锁等问题,都可以排查出来。

其他面试问题?

Q:垃圾回收是否涉及栈内存

  • 不会。因为栈内存无非就是一次次方法调用所产生的栈帧内存,而栈帧内存在每一次方法调用结束后,都会被弹出栈,即会自动被回收掉,所以根本不需要垃圾回收来管理我们的栈内存。
  • 垃圾回收只是去回收堆内存中无用的对象。

Q:栈内存分配越大越好吗

  • 栈内存划得越大,反而会让线程数变少。因为内存条物理内存大小是一定的,栈内存越大,那线程数就越少;
  • 一般栈内存划得大,只是为了进行多次的递归调用。一般采用系统默认大小就可以了,Linux下是1024kB,即1M大小;
  • 使用-Xss size可以指定栈内存的大小,如 -Xss256k(中间没有空格),把栈内存设为256k;

Q:方法内的局部变量是否线程安全

  • 如果方法内局部变量没有逃离方法的作用访问,它是线程安全的;
  • 如果是局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全;

3️⃣ 本地方法栈

它是个什么?

和虚拟机栈的作用非常相似,虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务

JVM在调用本地方法时,需要给这些本地方法提供的内存空间。本地方法不是由java代码编写的,因为java有时不能直接跟OS底层打交道,所以就需要用C或C++语言编写的本地方法来与OS底层的API打交道。java代码可以通过本地方法接口调用OS底层的功能。那这些本地方法运行时使用到的内存就是本地方法栈。

它有什么用?

本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、动态链接、出口信息。

方法执行完毕后相应的栈帧也会出栈并释放内存空间,也会出现 StackOverFlowErrorOutOfMemoryError 两种错误。

4️⃣ 堆(

标签:Java,虚拟机,区域,线程,内存,字符串,方法
From: https://www.cnblogs.com/afei688/p/16610677.html

相关文章

  • Java 常用类和方法
    JavaMath类xxxValue()方法用于将Number对象转换为 xxx 数据类型的值并返回。   Javaequals()方法equals()方法用于判断Number对象与方法的参数进是......
  • Java中的参数传递,到底是值传递还是引用传递?
    1、Java中的参数传递,到底是值传递还是引用传递?结论:Java只有值传递,没有引用传递!错误理解一:值传递和引用传递,区分的条件是传递的内容,如果是个值,就是值传递。如果......
  • 9.Java的LinkedList/Deque相关方法
    Java的LinkedList/Deque中add/offer/push,remove/pop/poll的区别它们来自不同的接口add/remove源自集合,所以添加到队尾,从队头删除;offer/poll源自队列(先进先出=>尾进......
  • Java学习 (26) 异常篇 异常结构&异常处理&自定义异常
    目录异常结构异常的定义异常的分类ErrorExpectionError和Exception的区别具体讲解视频异常处理捕获异常语法实例抛出异常语法实例具体讲解视频自定义异常具体讲解视频异常......
  • JAVA基础--程序流程控制--2022年8月21日
    第一节分支结构1、if分支的写法有几种,各有什么特点?if(){}if(){}  else{}if(){}  elseif(){} elseif(){} ... else{}......
  • 关于java变量的学习
    变量的概念内存中的一个存储区域 该区域的数据可以在同一类型范围内不断变化 变量是程序中最基本的存储单元  包含变量类型、变量名和存储的值使用变量注意J......
  • Java SE 15 新增特性
    JavaSE15新增特性作者:Grey原文地址:JavaSE15新增特性源码源仓库:Github:java_new_features镜像仓库:GitCode:java_new_featuresZGCJavaSE11JEP333将ZGC......
  • IDEA打包普通java项目并用java命令运行
    IDEA下打包为jar包,普通java项目(非web项目)效果是将第三方jar包放到一个文件夹中(如lib),这样看起来清晰一些。如下图这种:  1.项目结构。   1.关键:modules  ......
  • 关于Java 连接 MySQL 数据库报错:Failed to obtain JDBC Connection; ...: Communicati
    原因:是因为Java连接MySQL没有收到任何数据包,也就是说连接失败。解决方法:打开Windows服务程序,找到mysql进程,重启一下就可以了。......
  • 解决DOS系统运行JAVA程序乱码问题
    使用DOS程序运行java程序的时候,碰到乱码问题多数情况是:DOS支持GBK简体中文,不支持UTF-8,只要设置成UTF-8就可以啦。正确显示UTF-8字符,可以按照以下步骤操作:1、打开CMD.exe......