Java深度学习系列——JVM虚拟机内存划分

前言:

我是张哲,一位在互联网上不愿透露姓名的小学员,接下来大家看到的所有内容都是我背写的知识点,这里的知识点和你所学习到的不同,我中和了我的一些书籍和网上刷的面试笔记,相信这里能让你接触到更深入的知识点,我会慢慢的把我对某个知识点的理解写进去。

认识下JVM

JVM指的是Java的虚拟机,全称(Java Virtual Machine),我们可以通过java -version在命令行中查看Java的版本和当前虚拟机。比如:
java version “1.8.0_121”
Java™ SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot™ 64-Bit Server VM (build 25.121-b13, mixed mode)
这里我的JDK版本是1.8的,虚拟机用的是HotSpot ,它是官方提供的虚拟机,又叫做:热点探测技术。一般情况下中小公司都会使用,大公司也只是改了些HotSpot的参数而已。所以JVM虚拟机虽然分为多个版本但是很多技术都是通用的,本文主要阐述的是运行时数据区

JVM为我们开辟的运行时数据区共分为五部分:堆、虚拟机栈、本地方法栈、方法区、程序计数器

堆内存

堆:又分为三部分:新生代、老年代、永久代,在JDK1.8后分为:新生代、老年代,而永久代和方法区合并为占用物理内存的元空间,至于为什么要这么做,有什么好处,下次会针对这方面专门写个专题嘿。堆内存主要存放的是对象的空间就是new出来的那一部分,堆内存的空间容量远大于其他几部分。属于线程共享区域

虚拟机栈

虚拟机栈就是我们常说的栈内存,它存放的是方法在执行时开辟的空间,特点:先进后出,后进先出,由于它内部是一组连续的空间,所以效率比堆更高一些,但是虚拟机栈内存小,大概是1M左右,以实际空间为准。属于线程私有区域

本地方法栈

本地方法栈主要存放Java中用native修饰的方法的空间,称之为:本地方法,我们在看源码的时候看到了native就到底了,它内部就是C++的代码了,本区域作为了解即可。属于线程私有区域

方法区

这里主要存放的是类中的信息(可以简单的理解为类模板区域)、static区域、常量缓冲区(JDK1.7后将常量缓冲区存入堆内存中),方法区的空间不会像堆内存那样,它很难被回收,所以方法区也被称为:永久代。属于线程共享区域

程序计数器

是一块很小的区域,用于存储当前运行的线程所执行的字节码的行号指示器。当方法在虚拟机栈中执行时,会记录它实时虚拟机字节码指令的地址;简单的说就是实时的说明了方法执行时一步一步的做了什么开辟了什么空间等等。可以让线程切换前后,能保持代码本身的顺序执行。如果是native修饰的方法则开辟空间时程序计数器会给null。属于线程私有区域

这里呢抛出几个思考的问题:

1、对象分为属性和方法,但是为什么我们划内存空间的时候方法是在栈内存中的而没有在堆内存的对象空间内呢???
2、本文中说道的类模板区域指定是存放的是类的哪方面信息??
3、为什么经常发生栈内存溢出而很少发生堆内存溢出呢?
4、如果我们要在对某个方法执行递归操作,而方法中又要去new出对象空间,这样就导致了需要频繁的在堆中创建空间,但是这new出来的空间又很快成为“垃圾”,会加重GC的负担,那在这种场景下我们应该怎样优化呢??

问题一:

对象分为属性和方法,但是为什么我们划内存空间的时候方法是在栈内存中的而没有在堆内存的对象空间内呢???
这里其实对象空间在new出来时,会仿照类模板区域的样子形成空间,当然方法也在对象空间中,只不过方法在被调用的时候才会在栈内存中开辟空间而已。

问题二:

本文中说道的类模板区域指定是存放的是类的哪方面信息??
类模板区域主要是非static修饰的属性和方法,属性存放的是数据类型的默认值。比如以下代码:

public class Tasf{
	int a;
	boolean go;
	static double b;
	public static void fa(){
		
	}
	public void ma(){
		
	}
	public static void main(String[] args) {
						
	}
}

它在方法区开辟的空间中是这个样子:
在这里插入图片描述

问题三:

为什么经常发生栈内存溢出而很少发生堆内存溢出呢?
来看下面这段代码:

public class Tasf{
	int a;
	boolean go;
	public void fa(){
		Tasf t=new Tasf();
		fa();
	}
	public static void main(String[] args) {
		new Tasf().fa();
	}
}

方法fa()中去创建对象且再次调用fa(),调用方法的空间是在栈内存中开辟的,而new出来的空间是在堆内存中开辟的,而本代码中不断的new,不断的调用方法,那么堆内存和栈内存总会有一个先溢出导致程序错误从而被迫停止运行。
运行下发生:java.lang.StackOverflowError,栈内存溢出错误。
原因是栈内存比堆内存空间小的多,而且堆内存中GC会帮我们回收空间。

问题四:

如果我们要在对某个方法执行递归操作,而方法中又要去new出对象空间,这样就导致了需要频繁的在堆中创建空间,但是这new出来的空间又很快成为“垃圾”,会加重GC的负担,那在这种场景下我们应该怎样优化呢??
其实这个场景我们只需要想办法将new的空间转移到栈内存便可,这样每个方法执行完就会释放new出来的空间,但是如何做到呢???哈哈触碰到了我的知识盲区!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值