复习一下JVM内存结构
一、程序计数器
程序计数器内存很小,可以看作是当前线程所执行字节码的行号指示器。
有了它,程序就能被正确的执行。
因为有线程切换的存在,则每个线程必须有各自独立的程序计数器,即线程私有的内存。
这里再解释一下什么是线程切换,线程切换指的是:
单处理器在执行多线程时所进行的线程切换,多线程的交替运行会产生同时运行的错觉。
程序计数器不会发生OOM原因:
占用内存非常小,当线程结束时程序计数器也会随之回收。
二、本地方法栈与虚拟机栈
栈是stack的翻译,那stack又是什么?
在英文语境中,stack指的是一摞盘子堆叠起来、一摞书堆叠起来的这种状态,也就是 a stack of books. 借这种现实物理情境来描述计算机中的数据结构。
这种结构的特征就是LIFO, Last In First Out, 即后进先出。
也就是,一摞盘子,你只能一个一个往上堆,也只能一个一个从顶上往外取,对应**入栈和出栈(弹栈)**的操作。
以上是对栈这种结构的解释。
接下来说这两种栈结构:Native Method Stack 和 JVM stack.
栈是线程私有的,它的生命周期和线程是相同的。
栈里面保存栈帧。
什么是栈帧?
每个方法执行时都会创建一个栈帧。栈帧存储了局部变量表、操作数栈、动态连接和方法出口等信息。每个方法从调用到运行结束的过程,就对应着一个栈帧在栈中入栈到出栈的过程。
栈有可能出现什么异常?
StackOverflowError和OutOfMemoryError。
前者主要是深度递归和复杂嵌套方法调用造成。
后者的话发生在栈在进行动态扩展的时候,也就是说 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), 运行时常量池具备动态性,那怎么理解动态性呢?
就是说除了编译期产生的常量进入了常量池在类加载后又紧接着进入了运行时常量池以外,运行期间新的常量也会进入运行时常量池。
关于类加载的介绍我们再单独写一篇。
下面举一些例子把这里具体搞搞清楚。
常量池有什么用 ?
**优点:**常量池避免了频繁的创建和销毁对象而影响系统性能,其实现了对象的共享。
下面具体讲一下字符串常量池(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。
往期推荐:
● 学会@ConfigurationProperties月薪过三千
● 0.o?让我看看怎么个事儿之SpringBoot自动配置
复习一下JVM内存结构的更多相关文章
- JVM学习十二 - (复习)JVM内存结构
JVM 内存结构 Java 虚拟机的内存空间分为 5 个部分: 程序计数器 Java 虚拟机栈 本地方法栈 堆 方法区 JDK 1.8 同 JDK 1.7 比,最大的差别就是:元数据区取代了永久代.元 ...
- jvm系列(二):JVM内存结构
JVM内存结构 所有的Java开发人员可能会遇到这样的困惑?我该为堆内存设置多大空间呢?OutOfMemoryError的异常到底涉及到运行时数据的哪块区域?该怎么解决呢?其实如果你经常解决服务器性能 ...
- JVM内存结构之三--持久代
本文会介绍一些JVM内存结构的基本概念,然后很快会讲到持久代,来看下Java SE 8发布后它究竟到哪去了. 基础知识 JVM只不过是运行在你系统上的另一个进程而已,这一切的魔法始于一个java命令. ...
- JVM内存结构
前言 在Java语言开发过程中,out of memory错误是很常见的一种错误.对于JVM的内存结构有更深入的了解,更更好的帮我们排查此类问题,有效的避免此类问题发生.在JAVA 8中内存结构有进行 ...
- 管中窥豹——从对象的生命周期梳理JVM内存结构、GC调优、类加载、AOP编程及性能监控
如题,本文的宗旨既是透过对象的生命周期,来梳理JVM内存结构及GC相关知识,并辅以AOP及双亲委派机制原理,学习不仅仅是海绵式的吸收学习,还需要自己去分析why,加深对技术的理解和认知,祝大家早日走上 ...
- JVM内存结构,运行机制
三月十号,白天出去有事情出去了一天,晚上刚到食堂就接到阿里电话, 紧张到不行,很多基础的问题都不知道从哪里说了orz: 其中关于JVM内存结构,运行机制,自己笔记里面有总结的,可当天还是一下子说不出来 ...
- JVM内存结构简单认知
关于JVM的面试传送门:https://blog.csdn.net/shengmingqijiquan/article/details/77508471 JVM内存结构主要划分为:堆,jvm栈,本地方 ...
- 【JVM】JVM内存结构 VS Java内存模型 VS Java对象模型
原文:JVM内存结构 VS Java内存模型 VS Java对象模型 Java作为一种面向对象的,跨平台语言,其对象.内存等一直是比较难的知识点.而且很多概念的名称看起来又那么相似,很多人会傻傻分不清 ...
- 【转】JVM内存结构 VS Java内存模型 VS Java对象模型
JVM内存结构 我们都知道,Java代码是要运行在虚拟机上的,而虚拟机在执行Java程序的过程中会把所管理的内存划分为若干个不同的数据区域,这些区域都有各自的用途. 其中有些区域随着虚拟机进程的启动而 ...
- jvm系列二、JVM内存结构
原文链接:http://www.cnblogs.com/ityouknow/p/5610232.html 所有的Java开发人员可能会遇到这样的困惑?我该为堆内存设置多大空间呢?OutOfMemory ...
随机推荐
- Java中有哪些方式能实现锁某个变量
有的时候博客内容会有变动,首发博客是最新的,其他博客地址可能会未同步,认准https://blog.zysicyj.top 首发博客地址 系列文章地址 在Java中,有几种方式可以实现对某个变量的锁定 ...
- [转帖]redis-benchmark的使用总结
redis-benchmark的使用总结 Redis简介: 测试需求: 测试环境架构 测试工具Redis-benchmark 1 redis-benchmark使用方法 参数的作用 2 测试查看 测试 ...
- [转帖]linux设置page cache大小,Linux Page Cache调优在Kafka中的应用
本文首发于 vivo互联网技术 微信公众号 链接: 作者:Yang Yijun 本文主要描述Linux Page Cache优化的背景.Page Cache的基本概念.列举之前针对Kafka的 IO ...
- [转帖]oracle rac后台进程和LMS说明
本文摘抄录oracle官方文档,oracle rac使用的后台进程,用以备忘,记录之. About Oracle RAC Background Processes The GCS and GES pr ...
- 树状数组(区间修改&&区间查询)
#include<bits/stdc++.h> #define int long long using namespace std; int n,m,x,x1,y,z; int a[100 ...
- Leetcode 92题反转链表 II(Reverse Linked List II) Java语言求解
前言 反转链表可以先看我这篇文章: Leetcode 206题 反转链表(Reverse Linked List)Java语言求解 题目链接 https://leetcode-cn.com/probl ...
- [2] 以逆向的角度来看流程控制语句——switch
[2] 以逆向的角度来看流程控制语句--switch 1. switch分支数小于4 汇编标识: 00401021 mov [ebp-4], ecx 00401024 cmp dword ptr [e ...
- SqlSugar的Where用法
1.普通表达式查询 //id=@id var list=db.Queryable<Student>().Where(it => it.Id == id).ToList(); // ...
- 【6】VScode 无法在终端输入问题,提示:无法在只读编辑器中编辑
相关文章: [1]VScode中文界面方法-------超简单教程 [2]VScode搭建python和tensorflow环境 [3]VSCode 主题设置推荐,自定义配色方案,修改注释高亮颜色 [ ...
- 7.5 C/C++ 实现链表队列
链表队列是一种基于链表实现的队列,相比于顺序队列而言,链表队列不需要预先申请固定大小的内存空间,可以根据需要动态申请和释放内存.在链表队列中,每个节点包含一个数据元素和一个指向下一个节点的指针,头节点 ...