jvm新年快乐,这篇jvm就当做新年礼物送给你吧---jvm第一集
Posted 温文艾尔
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jvm新年快乐,这篇jvm就当做新年礼物送给你吧---jvm第一集相关的知识,希望对你有一定的参考价值。
⭐️写在前面
这里是温文艾尔の学习之路
- 👍如果对你有帮助,给博主一个免费的点赞以示鼓励把QAQ
- 👋博客主页🎉 温文艾尔の学习小屋
- ⭐️更多文章👨🎓请关注温文艾尔主页📝
- 🍅文章发布日期:2021.12.29
- 👋java学习之路!
- 欢迎各位🔎点赞👍评论收藏⭐️
- 🎄新年快乐朋友们🎄
- 👋jvm学习之路!
- 🔎更多文章(以下redis文章均上CSDN热榜):
- ⭐️【Redis二三事】一套超详细的Redis学习教程(步骤图片+实操)—第一集
- ⭐️【Redis二三事】一套超详细的Redis学习教程(步骤图片+实操)—第二集
- ⭐️【源码那些事】超详细的ArrayList底层源码+经典面试题
- ⭐️HashMap底层红黑树原理(超详细图解)+手写红黑树代码
- ⭐️HashMap底层源码解析上(超详细图解+面试题)
- ⭐️HashMap底层源码解析下(超详细图解)
⭐️属实好久不见了大伙们,👍首先恭喜自己荣获CSDN的JAVA领域博客新星👨🎓
在获得荣誉之后这将近20天温文艾尔干嘛去了呢,答案是我在搭建个人博客网站哈哈,👋于是停更了这么长时间,好消息是随着项目的结束,我的博客网站很快将要和大家见面了,从今天开始对jvm内容的更新,不久后会做一篇关于个人博客网站的文章
🎄如果文章对大家有帮助,欢迎点赞👍收藏⭐️评论📝支持,小温在这里谢过大家了,在这里祝福大家新年快乐!🎄
笔记整理于传智播客黑马程序员-jvm和小滴课堂,老师讲的细致易懂
文章目录
- ⭐️1.JVM入门
- ⭐️2.Program Counter Register 程序计数器(寄存器)
- ⭐️3.Java Virtual Machine Stacks(java虚拟机栈)
- ⭐️面试题
- ⭐️本地方法栈
- ⭐️5.堆
⭐️1.JVM入门
⭐️1.1 jvm定义
Java Virtual Machine - java程序的运行环境(java二进制字节码的运行环境)
⭐️1.2 jvm优势
-
一次编写,到处运行的基石
-
自动内存管理,垃圾回收机制
java语言出现时机比较早,同一时期它的竞争对手是c和c++,而c和c++是没有垃圾回收功能的,需要程序员自己
释放内存,如果编码不当,很容易造成内存的泄漏,java虚拟机的垃圾回收功能减少了程序员的负担
- 数组下标越界检查
- 多态
⭐️1.3 JVM,JRE,JDK的比较
JVM运行时数据区
常见的JVM
⭐️2.Program Counter Register 程序计数器(寄存器)
程序计数器在jvm中的位置
程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器
字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令
,分支
,循环
,跳转
,异常处理
,线程恢复
等基础功能都需要依赖这个计数器来完成
程序计数器
作用:
记住下一条jvm指令的执行地址
特点:
1.是线程私有的
每个线程都有自己的程序计数器线程切换执行的过程中,为了准确知道指令执行到哪里,需要程序计数器发功
2.不会存在内存溢出(堆栈等都会出现内存溢出)
⭐️3.Java Virtual Machine Stacks(java虚拟机栈)
java虚拟机栈是作用于方法执行的一块java内存区域
栈在jvm中的位置
栈区
-
每个线程包含一个栈区,栈中只保存
基础数据类型
的对象和自定义对象的引用
(不是对象),对象都存放在堆区中 -
每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。
-
栈分为3个部分:
基本类型变量区
、执行环境上下文
、操作指令区
(存放操作指令).
- 每个线程运行时所需要的内存,称为虚拟机栈
-
- 每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存,方法内有参数,局部变量,返回地址等
- 都需要占用内存
-
- 每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法
栈帧:每个方法运行时需要的内存
当调用第一个方法时,栈就给第一个方法划分栈帧空间,并且把它压入栈内,当方法执行完毕,方法对应的栈帧出栈,释放方法所占用的内存
栈演示
package day01;
/**
* Description
* User:
* Date:
* Time:
*/
public class StackTest
public static void main(String[] args)
method1();
private static void method1()
method2(1,2);
private static int method2(int a,int b)
int c = a+b;
return c;
首先执行method1,method1对应的栈帧入栈,称为活动栈帧
继续执行,method1调用method2,method2对应的栈帧入栈,成为活动栈帧
method2执行完毕,method2对应的栈帧被释放掉,method1方法对应的栈帧称为活动栈帧
最后method1执行完毕,main方法对应的栈成为活动栈帧,当main方法执行完毕出栈,虚拟机栈空,方法全部结束
⭐️面试题
1.垃圾回收机制是否涉及栈内存?
不涉及,栈内存涉及的是一次一次的方法调用所产生的栈帧内存,而栈帧内存在每一次的方法调用后都会被弹出栈,被自动回收掉,所以不需要垃圾回收来管理栈内存,垃圾回收主要回收堆内存中的无用对象
2.栈内存分配越大越好吗?
栈内存可以通过虚拟机参数来进行指定 -Xss size
操作系统的栈默认大小
我们物理内存的大小是一定
的,如果我们栈内存分的大,则线程数会变少
,加入我们一个线程所用1兆内存,物理内存假设有500兆,我们理论上可以有500个线程同时运行
,但如果设置每个线程的栈内存为2兆,那么理论上我们只能同时运行250个线程,线程数变少
故栈内存不是越大越好,栈内存大只是能够进行更多次的方法递归调用,而不会增强运行的效率,反而会导致线程数目的变少
3.方法内的局部变量是否线程安全?
我们举个例子
int x = 0;
for (int i=0;i<5000;i++)
x++;
System.out.println(x);
当多个线程同时执行该方法,上面代码会不会造成x值的混乱?
不会,x变量是方法内的局部变量,一个线程对应一个栈,线程内每次方法调用都会产生新的栈帧
可以看到,在每个线程都有自己私有的一个x变量
,运行时互不干扰,如果执行上述方法,会同时+5000
但是当我们把x改为静态变量,结果就会不一样,static修饰的变量为两个方法所共享,并不私有
线程执行x++后,每次自增都会将结果写回静态区,如果不加线程保护,就会产生线程安全的问题
我们再看一个案例
package day01;
/**
* Description
* User:
* Date:
* Time:
*/
public class ThreadDemo
public static void main(String[] args)
public static void m1()
StringBuilder sb = new StringBuilder();
sb.append(1);
sb.append(2);
sb.append(3);
System.out.println(sb.toString());
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;
m1方法不会出现线程安全问题,因为StringBuilder sb是我们方法内的一个局部变量,是私有的,其他线程不可能同时访问到该对象
m2方法不是线程安全的,因为StringBuilder sb是作为方法参数传入,有可能有其他的线程可以访问到它,它对其他线程是共享的
m3方法也不是线程安全的,StringBuilder sb虽然是方法内的局部变量,但是他作为一个方法的结果返回,便逃离了m3对它的作用范围
因此我们得出结论
- 如果方法内的局部变量没有逃离方法的作用范围,它就是线程安全的
- 如果局部变量引用了对象并逃离了方法的作用范围,他就是非线程安全的,如果是(int ,float,double)等非引用对象,即使逃离了方法的作用范围,也是线程安全的
⭐️4.栈内存溢出
1.栈帧过多导致栈内存溢出,例如方法递归调用没有结束标志
2.栈帧过大导致栈内存溢出
下面我们结合两个具体的案例来看栈溢出的几个场景
public class StackDemo01
private static int count;
public static void main(String[] args)
try
method1();
catch (Throwable e)
e.printStackTrace();
System.out.println(count);
private static void method1()
count++;
method1();
输出结果:
java.lang.StackOverflowError
。。。
20706
通过设置虚拟机参数来验证
重新运行,查看结果
java.lang.StackOverflowError
。。。
2719
设置虚拟机参数之后,我们设置栈的总大小变小了,递归2719次便结束了
⭐️本地方法栈
我们平常见到的带有native关键字的方法都是用c/c++来实现,我们java代码通过本地的native方法接口去间接的调用真正的c/c++的实现
例如clone()方法
hashCode()方法
⭐️5.堆
1.堆是java内存区域中一块用来存放对象实例的区域,几乎所有的对象实例都在这里分配内存,此内存区域的唯一
目的就是存放对象实例
2.存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令)
3.jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身
4.java堆是垃圾收集器管理的主要区域,因此很多时候也被称作GC“堆”
5.java堆可以分成新生代和老年代,新生代可分为To Space、From Space、Eden
特点
- 它是线程共享的,堆中对象都需要考虑线程安全的问题
- 有垃圾回收机制
堆内存溢出问题
对象被当作垃圾回收的条件是不再使用它,那么如果不断地产生对象,产生的新对象仍然有人在使用它们,这意味着
这些对象不能作为垃圾被处理,这样的对象达到一定的数量就会产生堆内存溢出问题
下面我们看一个具体的案例
public class Demo01
public static void main(String[] args)
int i=0;
try
ArrayList<Object> list = new ArrayList<>();
String a = "hello";
while (true)
list.add(a);
a=a+a;
i++;
catch (Throwable e)
e.printStackTrace();
System.out.println(i);
运行结果:
java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3332)
at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448)
at java.lang.StringBuilder.append(StringBuilder.java:136)
at day01.Demo01.main(Demo01.java:19)
java.lang.OutOfMemoryError: Java heap space
java堆空间不足导致的异常
堆内存诊断
1.jps工具
- 查看当前系统中有哪些java进程
2.jmap工具
- 查看堆内存占用情况
3.jconsole工具
- 图形界面的,多功能的监测工具,可以连续监测
以上是关于jvm新年快乐,这篇jvm就当做新年礼物送给你吧---jvm第一集的主要内容,如果未能解决你的问题,请参考以下文章