java创建对象new后面为啥可以传入参数_理解Java虚拟机#3 Java内存分配

f7edc18dde7347601a0cfbafe99ba433.png

Java内存分配

一、运行时数据区域

众所周知,程序的运行要把数据和代码装入到内存中运行,所以明白程序执行过程中内存是如何分配的是很有必要的。 Java虚拟机中,根据不同对象的特点,将内存划分为不同的数据区,如下图:

876dcd427b2143110fde44977e71ba09.png

其中,方法区和堆区是所有线程共享的区域,随着虚拟机进程的启动而存在。

栈区和PC是线程私有的,随着用户线程的启动和消亡。

1.1 程序计数器(PC)

与OS中PC的作用一样,每个线程私有的PC用来指示下一条要执行的字节码指令。 所以分支、循环等流程的实现都需要依靠PC来实现

1.2 Java虚拟机栈

Java虚拟机栈是线程私有的,生命周期与线程相同。

每个方法在执行的同时创建一个栈帧(Stack Frame),用于存储局部变量表、操作数栈、动态链接、方法出口等信息。

每个方法被调用时,入栈,方法结束时,退栈。

局部变量表存放着编译期间可以知道的数据类型(char boolean等)

局部变量表所需的内存空间在编译期间完成分配,也就是说当一个方法分配栈帧的时候,大小是提前确定的,方法运行期间不会改变局部变量表的大小。

1.3 本地方法栈

作用于Java虚拟机栈类似

当执行Java方法时,使用的是Java虚拟机栈

执行本地方法时,使用的是本地方法栈

1.4 堆

Java堆是Java虚拟机所管理的内存最大的一块,所有new出来的对象都在这里分配。

堆是所有线程共享的,所以GC时主要是对堆里面的对象进行回收。

为了GC时方便,将内存的堆分为新生代老年代,新生代的对象生命周期更新频繁,每次都有大量的对象死去,少量的存活,老年代的对象生命周期比较长,每次存活率都比较高。

同时,堆区在物理内存上是不连续的,只是逻辑上连续。

1.5 方法区

方法区是所有线程共享的。 用来存储加载的类信息、常量、静态变量、即时编译器后的代码等数据。

1.6 运行时常量池

常量池是方法区的一部分

用来存放编译期间或者运行期间产生的常量。

1.7 直接内存

直接内存不属于虚拟机运行时内存的一部分

而是在本机当中剩余内存分配出来的一块空间,通过一些传递数据的手段,从外内存到Java内存进行传递数据。 其大小受到主机内存大小的限制。

二、Java内存分配

根据以上特点,可以总结如下:

  1. 栈:存放基本数据类型,对象的引用。注意对象的引用是放在栈中,而对象本身是放在堆中(new出来的对象)或者常量池中(字符串常量对象)
  2. 堆:存放所有new出来的对象
  3. 常量池:存放字符串常量和基本数据类型的常量(public static final)
  4. 方法区:放静态常量等(static)

2.1 String常量和引用的分配

如下面代码的内存分配:

String 

0b29148acbca479d854190b4c1c01c3f.png

首先字符串常量分配在常量池当中,其引用s1等在栈中

new出来的string对象分配在堆中,其引用s4等在栈中

所以可以看出,s1 s2 s3指向的是同一个字符串常量,而s4 s5 s6则不是

这里有一个细节需要注意,new出来的“Wang”对象,会首先去常量池中查找是否已经有“Wang”对象,如果没有则在常量池创建一个,再复制到堆中。

2.2 基础类型的变量和常量

int 

fff571cbe3499e1f05bb760212352434.png

基础的数据类型直接存放在栈中

stactic final的存放在常量池当中

2.3 成员变量和局部变量

局部变量(包括形式参数)分配在栈中,随着方法的消失而消失

成员变量存储在堆中,有GC负责回收

public 

从main方法的执行分析:

1. int date = 9;

date属于局部变量,此时分配在栈中。

2. TestPeople testPeople = new ();

testPeople 属于引用,分配在栈中

对象new TestPeople()分配在堆中

3. testPeople .change(date);

i属于局部变量,存放在栈中,随着change方法的结束而消失,因此,在这里并不会改变传入date的值

4. People wang= new People(7, 7,1970);

wang是对象引用,存放在栈中

对象new People(7,7,1970)存放在堆中

传入的7 7 1970 对应的d m y形参属于局部变量,存放在栈中,当构造方法执行完毕时自动消失

People对象中的day month year为成员变量,分配在堆中

5. main方法结束时

date变量 testPeople wang引用都从栈中消失

此时new出来的TestPeople People没有引用指向他们,等待被GC

d06238f3293200a4a9c4c3a14501e30a.png

三、内存溢出

3.1 Java堆溢出

根据堆的特性,Java堆主要存放new出来的对象。

所以只要不断的创建对象,并且保持GC Roots到对象之间有可达路径就可以避免这些对象被GC,超过堆的最大容量之后就会造成内存溢出(OutOfMemoryError)。

import 

3.2 栈溢出

我们知道栈分配的是局部变量和每一个方法的栈帧,所以不断地调用方法,可以造成StackOverflowError

public 

3.3 方法区和常量池溢出

常量区存放字符串常量等,所以这我们的思想就是在常量池中不断产生字符串常量。

这里我们借助String.intern()方法:如果字符串常量池中已经包含一个此String对象的字符串,则返回常量池中这个对象;否则,将此String对象添加到常量池中并返回其引用。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值