Java内存模型规定所有的变量都是存在主存中,每个线程都有自己的工作内存。线程堆变量的操作都必须在工作内存进行,不能直接堆主存进行操作,并且每个线程不能访问其他线程的工作内存。
Java内存模型的Volatile关键字,原子性、可见性、有序性
虚拟机栈:用来放局部变量、堆区对象的引用和常量池对象的引用;但对象本身不存放在栈中,而是存放在堆(new出来的对象)或者常量池中(对象可能在常量池里)(字符串常量对象存放在常量池中。);
方法区:存放类的信息;此区包含常量池(常量池用来放基本类型常量和字符串类型常量),此部分可以回收;方法区可以放用static修饰的变量,但此部分不能回收,因为方法区也叫持久带,永久带基本不参与垃圾回收;
堆::存放一些new出的对象(包含成员变量),和数组。但发生了就不会保存堆里面了。
java对象创建分为两个过程:声明对象引用和创建对象实体。类信息、对象引用、对象实体均在内存的不同区域。
每一个java应用程序均会唯一的对应一个jvm实例,而这个jvm实例将会完成对象的内存分配、程序运行、垃圾回收等工作。JVM将其内存分为三个区域:方法区、栈区、堆区。
步骤:1、将JVM加载到内存
2、jvm首先检查public类是否存在,存在就会将它加载到内存方法区中,否则抛出异常,结束进程
3、对类中静态字段进行初始化(显示初始化就为指定的值,否则为默认值),接着执行静态代码块
4、在栈中创建局部变量,堆中创建对象实体,为堆对象实体的成员变量(非静态)初始化,对于静态字段只需要从方法区的静态区中引用其值即可,执行构造代码块,接着执行构造函数(当然,调用顺序会一直上溯到Object类,每一个构造方法的第一行为super()语句),最后将对象实体堆地址给栈中引用变量
要注意的是,实例字段包括自身定义的和从父类继承下来的(即使父类的实例字段被子类覆盖或者被private修饰,都照样为其分配内存)
C++中的引用和JAVA的引用作对比,其实他们两个只是“名称”一样,本质并没什么关系,C++中的引用只是给现存变量起了一个别名(引用变量只是一个符号引用而已,编译器并不会给引用分配新的内存),而JAVA中的引用变量却是真真正正的变量,具有自己的内存空间,只是不同的引用变量可以“指向”同一个对象而已。因此,如果要拿C++和JAVA引用对象的方式相对比,C++中的指针倒和JAVA中的引用如出一辙,毕竟,JAVA中的引用其实就是对指针的封装
本文介绍Java内存模型中的三个代:年轻代,终身代以及永久代。文中图示了各个代的默认排列。本文适用于JDK 1.4到JDK6。
之前也介绍过,下面主要介绍Java内存模型中的三个代。Java的内存模型由3个代组成,各个代的默认排列有如下图(适用JDK1.4.* 到 JDK6):
Java 的内存模型分为
叫法不同,表达的意思却是基本相同。
注意Young(年轻代)还可以分为Eden区和两个Survivor区(from和to,这两个Survivor区大小严格一至),新的对象实例总是首先放在Eden区,Survivor区作为Eden区和Tenure(终生代)的缓冲,可以向Tenure(终生代)转移活动的对象实例。
Tenure(终生代)中存放生命周期长久的实例对象,但并不是如它的名字那样是终生的,里面的对象照样会被回收掉。
Perm(永久代)则是非堆内存的组成部分。主要存放加载的Class类级对象如class本身,method,field等等。
有同学可能已经注意到了,每个代都有的Virtual区又是什么?
我们知道有一些参数可以影响以上各代的大小。
在JVM启动时,就已经保留了固定的内存空间给Heap内存,这部分内存并不一定都会被JVM使用,但是可以确定的是这部分保留的内存不会被其他进程使用。这部分内存大小由 -Xmx 参数指定。
而另一部分内存在JVM启动时就分配给JVM,作为JVM的初始Heap内存使用。影响这个的参数是 -Xms ,如果 -Xms 指定的值比-Xmx 的小,那么两者的差值就是Virtual内存值。随着程序的运行,Eden区、 Tenured区和Perm区会逐渐使用保留的Virtual空间。
|
---|
可以看到堆内存默认值最大不会超过1G。
使用堆内存空闲百分比来定义,一般在32位机器上的默认值如下:
当空闲堆内存所占堆内存百分比低于40%,JVM就会试图扩张堆内存空间;当空闲堆内存所占堆内存百分比高于70%,JVM就会试图压缩堆内存空间。
ps:以上默认值在不同平台会有不同的值,如果是64位系统,这些值一般需要扩张30%,来容纳在64位系统下变大的对象。
32位系统下默认值如下:
|
---|
以上就介绍了Java内存模型的三个代。