一、程序计数器

程序计数器内存很小,可以看作是当前线程所执行字节码的行号指示器

有了它,程序就能被正确的执行。

因为有线程切换的存在,则每个线程必须有各自独立的程序计数器,即线程私有的内存。

这里再解释一下什么是线程切换,线程切换指的是:

单处理器在执行多线程时所进行的线程切换,多线程的交替运行会产生同时运行的错觉。

程序计数器不会发生OOM原因:

占用内存非常小,当线程结束时程序计数器也会随之回收。

二、本地方法栈与虚拟机栈

栈是stack的翻译,那stack又是什么?

在英文语境中,stack指的是一摞盘子堆叠起来、一摞书堆叠起来的这种状态,也就是 a stack of books. 借这种现实物理情境来描述计算机中的数据结构。

这种结构的特征就是LIFO, Last In First Out, 即后进先出

也就是,一摞盘子,你只能一个一个往上堆,也只能一个一个从顶上往外取,对应**入栈和出栈(弹栈)**的操作。

以上是对栈这种结构的解释。

接下来说这两种栈结构:Native Method Stack 和 JVM stack.

栈是线程私有的,它的生命周期和线程是相同的。

栈里面保存栈帧

什么是栈帧

每个方法执行时都会创建一个栈帧。栈帧存储了局部变量表、操作数栈、动态连接和方法出口等信息。每个方法从调用到运行结束的过程,就对应着一个栈帧在栈中入栈到出栈的过程

栈有可能出现什么异常?

StackOverflowErrorOutOfMemoryError

前者主要是深度递归和复杂嵌套方法调用造成。

后者的话发生在栈在进行动态扩展的时候,也就是说 jvm 实现中栈的大小此时是不固定的,因为线程操作需要更多的栈空间而在申请内存的时候失败就会抛出OutOfMemoryError错误。

JVM 中的栈包括 Java 虚拟机栈和本地方法栈。

两者的区别就是:

Java 虚拟机栈为 JVM 执行 Java 方法服务,本地方法栈则为 JVM 使用到的 Native 方法(比如C或C++)服务。

四、堆

Heap有什么特征?

  • • JVM管理的最大内存区域

  • • 线程共享

  • • 存放对象实例

  • • 内存空间可以物理上不连续

  • • 垃圾回收的主要区域

关于垃圾回收的内容这里不展开讲了。

五、方法区

JVM规范把方法区描述为堆的一个逻辑部分,但有一个别名叫Non-Heap,即Heap中的Non-heap, 也是说明它和堆其实还是不一样的。

方法区有什么特征?

• 线程共享

• 存放已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据

• 垃圾回收较少出现,甚至可选择不进行垃圾回收

方法区的垃圾回收主要针对常量池的回收和对类的卸载

这里主要说一下运行时常量池:

Class文件中除了有类的版本、字段、方法、接口等描述信息外还有一项常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。

这里出现了两个常量池,它们是不一样的,一个叫常量池,一个叫运行时常量池(Runtime Constant Pool), 运行时常量池具备动态性,那怎么理解动态性呢?

就是说除了编译期产生的常量进入了常量池在类加载后又紧接着进入了运行时常量池以外,运行期间新的常量也会进入运行时常量池。

关于类加载的介绍我们再单独写一篇。

《学一点关于JVM类加载的知识》

下面举一些例子把这里具体搞搞清楚。

常量池有什么用 ?

**优点:**常量池避免了频繁的创建和销毁对象而影响系统性能,其实现了对象的共享。

下面具体讲一下字符串常量池(String常量池):

String 是由 final 修饰的类,是不可以被继承的。通常有两种方式来创建对象。

//1、这种存在Heap中,每次new都会创建一个全新对象
String str = new String("abcd");
 
//2、这种是在栈上创建对象引用变量str,指向字符串常量池中的“abcd”(没有的话新建一个)
String str = "abcd";

关于字符串 + 号连接问题:

对于字符串常量的 + 号连接,在程序编译期,JVM就会将其优化为 + 号连接后的值。所以在编译期其字符串常量的值就确定了。

String a = "a1";   
String b = "a" + 1;   
System.out.println((a == b)); //result = true  
 
String a = "atrue";   
String b = "a" + "true";   
System.out.println((a == b)); //result = true 
 
String a = "a3.4";   
String b = "a" + 3.4;   
System.out.println((a == b)); //result = true 

关于字符串引用 + 号连接问题:

对于字符串引用的 + 号连接问题,由于字符串引用在编译期是无法确定下来的,在程序的运行期动态分配并创建新的地址存储对象

    public static void main(String[] args){
       String str1 = "a";
       String str2 = "ab";
       String str3 = str1 + "b";
       System.out.print(str2 == str3);//false
    }

通过 jad 反编译工具,分析上述代码到底做了什么。

    public class TestDemo{
    public TestDemo(){
    }
    public static void main(String args[]){
        String s = "a";
        String s1 = "ab";
        String s2 = (new StringBuilder()).append(s).append("b").toString();
        System.out.print(s1 = s2);
    }
}

发现 new 了一个 StringBuilder 对象,然后使用 append 方法优化了 + 操作符。new 在上创建对象,而 String s1=“ab”则是在常量池中创建对象,两个应用所指向的内存地址是不同的,所以 s1 == s2 结果为 false。

这里引出一个实际开发中关于字符串拼接的问题。就是尽量不要在 for 循环中使用 + 号来操作字符串

因为如果用“+”号的话,每次循环都会创建和销毁一个StringBuilder对象,这样还不如在循环外创建一个StringBuilder对象,然后使用append方法。

    public static void main(String[] args){
        StringBuilder s = new StringBuilder();
        for(int i = 0; i < 100; i++){
            s.append("a");
        }
    }

使用final修饰的字符串

    public static void main(String[] args){
        final String str1 = "a";
        String str2 = "ab";
        String str3 = str1 + "b";
        System.out.print(str2 == str3);//true
    }

final 修饰的变量是一个常量,编译期就能确定其值。所以 str1 + "b"就等同于 "a" + "b",所以结果是 true。

String对象的intern方法。

    public static void main(String[] args){
        String s = "ab";
        String s1 = "a";
        String s2 = "b";
        String s3 = s1 + s2;
        System.out.println(s3 == s);//false
        System.out.println(s3.intern() == s);//true
    }

通过前面学习我们知道,s1+s2 实际上在堆上 new 了一个 StringBuilder 对象,而 s 在常量池中创建对象 “ab”,所以 s3 == s 为 false。

但是 s3 调用 intern 方法,返回的是s3的内容(ab)在常量池中的地址值。所以 s3.intern() == s 结果为 true。


往期推荐:

● 师爷,翻译翻译什么叫AOP

终于搞懂动态代理了!

● 学会@ConfigurationProperties月薪过三千

● 0.o?让我看看怎么个事儿之SpringBoot自动配置

● 不是银趴~是@Import!

● Java反射,看完就会用

复习一下JVM内存结构的更多相关文章

  1. JVM学习十二 - (复习)JVM内存结构

    JVM 内存结构 Java 虚拟机的内存空间分为 5 个部分: 程序计数器 Java 虚拟机栈 本地方法栈 堆 方法区 JDK 1.8 同 JDK 1.7 比,最大的差别就是:元数据区取代了永久代.元 ...

  2. jvm系列(二):JVM内存结构

    JVM内存结构 所有的Java开发人员可能会遇到这样的困惑?我该为堆内存设置多大空间呢?OutOfMemoryError的异常到底涉及到运行时数据的哪块区域?该怎么解决呢?其实如果你经常解决服务器性能 ...

  3. JVM内存结构之三--持久代

    本文会介绍一些JVM内存结构的基本概念,然后很快会讲到持久代,来看下Java SE 8发布后它究竟到哪去了. 基础知识 JVM只不过是运行在你系统上的另一个进程而已,这一切的魔法始于一个java命令. ...

  4. JVM内存结构

    前言 在Java语言开发过程中,out of memory错误是很常见的一种错误.对于JVM的内存结构有更深入的了解,更更好的帮我们排查此类问题,有效的避免此类问题发生.在JAVA 8中内存结构有进行 ...

  5. 管中窥豹——从对象的生命周期梳理JVM内存结构、GC调优、类加载、AOP编程及性能监控

    如题,本文的宗旨既是透过对象的生命周期,来梳理JVM内存结构及GC相关知识,并辅以AOP及双亲委派机制原理,学习不仅仅是海绵式的吸收学习,还需要自己去分析why,加深对技术的理解和认知,祝大家早日走上 ...

  6. JVM内存结构,运行机制

    三月十号,白天出去有事情出去了一天,晚上刚到食堂就接到阿里电话, 紧张到不行,很多基础的问题都不知道从哪里说了orz: 其中关于JVM内存结构,运行机制,自己笔记里面有总结的,可当天还是一下子说不出来 ...

  7. JVM内存结构简单认知

    关于JVM的面试传送门:https://blog.csdn.net/shengmingqijiquan/article/details/77508471 JVM内存结构主要划分为:堆,jvm栈,本地方 ...

  8. 【JVM】JVM内存结构 VS Java内存模型 VS Java对象模型

    原文:JVM内存结构 VS Java内存模型 VS Java对象模型 Java作为一种面向对象的,跨平台语言,其对象.内存等一直是比较难的知识点.而且很多概念的名称看起来又那么相似,很多人会傻傻分不清 ...

  9. 【转】JVM内存结构 VS Java内存模型 VS Java对象模型

    JVM内存结构 我们都知道,Java代码是要运行在虚拟机上的,而虚拟机在执行Java程序的过程中会把所管理的内存划分为若干个不同的数据区域,这些区域都有各自的用途. 其中有些区域随着虚拟机进程的启动而 ...

  10. jvm系列二、JVM内存结构

    原文链接:http://www.cnblogs.com/ityouknow/p/5610232.html 所有的Java开发人员可能会遇到这样的困惑?我该为堆内存设置多大空间呢?OutOfMemory ...

随机推荐

  1. Angular系列教程之管道

    .markdown-body { line-height: 1.75; font-weight: 400; font-size: 16px; overflow-x: hidden; color: rg ...

  2. [转帖]OceanBase实验4:迁移MySQL数据到OceanBase集群

    服务器环境 1)12核48G,操作系统为centos 7.9系统,单节点三副本1-1-1集群. 2)源MySQL数据库:与OceanBase同一台服务器,版本为MySQL 5.7. 1.使用 mysq ...

  3. [转帖]Nginx 保留 Client 真实 IP

    https://lqingcloud.cn/post/nginx-01/#:~:text=%E5%9C%A8%20Nginx%20%E4%B8%AD%E5%8F%AF%E4%BB%A5%E9%80%9 ...

  4. [转帖]《Linux性能优化实战》笔记(十五)—— 磁盘IO的工作原理

    前一篇介绍了文件系统的工作原理,这一篇来看看磁盘IO的工作原理 一. 磁盘 1. 按存储介质分类 磁盘是可以持久化存储的设备,根据存储介质的不同,常见磁盘可以分为两类:机械磁盘和固态磁盘. 机械磁盘, ...

  5. [转帖]CTF -bugku-misc(持续更新直到全部刷完)

    CTF -bugku-misc(持续更新直到全部刷完) https://www.cnblogs.com/cat47/p/11432475.html 1.签到题 点开可见.(这题就不浪费键盘了) CTF ...

  6. [转帖]jmeter(八)断言

    https://www.cnblogs.com/pachongshangdexuebi/p/11582891.html jmeter中有个元件叫做断言(Assertion),它的作用和loadrunn ...

  7. 【转帖】nginx变量使用方法详解-5

    https://www.diewufeiyang.com/post/579.html 前面在 (二) 中我们已经了解到变量值容器的生命期是与请求绑定的,但是我当时有意避开了"请求" ...

  8. 小程序字节转GBK及UTF8

    前段时间在Android原生搞的BLE扫码枪又要移植到小程序上来.本以为小程序不支持BLE的,结果一搜,还真支持-_-|| . 蓝牙部分问题不大,遇到的主要问题是,小程序环境如何对字符编码进行判断以及 ...

  9. Markdown常用书写语法合集

    1. 文字设置 1.1 文字颜色 中常用的文字颜色有: 红色文字:<font color="red">红色文字</font> 浅红色文字:<font ...

  10. Linux 系统下查找文件命令

    Linux 系统下查找文件命令,融合多部Linux经典著作,去除多余部分,保留实用部分. 查命令绝对路径: which用于查找并显示给定命令的绝对路径,环境变量中PATH参数也可以被查出来. [roo ...