jvm内存分布结构
1.内存分布情况
jvm内存分为堆,方法区,栈
1.1堆
堆内存分为young generation(新生区)和old generation(年老区)
新生区内存分为eden,form survivor,to survivor以8:1:1的内存大小存储
保存:保存对象实例的属性值,属性的类型和对象本身的类型标记等,并不保存对象的方法,实例变量
主要保存:
1.2方法区
方法区包含
1.类的信息:完整有效名;直接父类的完整有效名(除非这个类型是interface或是
java.lang.Object,两种情况下都没有父类) ;修饰符(public,abstract, final的某个子集)
2.常量池 :到的常量的一个有序集合,包括实际的常量(string,
integer, 和floating point常量)和对类型,域和方法的符号引用
3.域信息 :域修饰符(public, private, protected,static,final volatile, transient的某个子集)
4.方法信息 :方法名 ;方法的返回类型(或 void) ;方法参数的数量和类型(有序的);方法的修饰符
5.类变量:类的静态变量(static)
6.常量:final变量或属性;
7.对Class类的引用
1.3栈(方法运行期间,局部变量表大小不变)
局部变量区、操作数栈、帧数据区组成,以栈帧的形式存放本地方法的调用状态(包括方法调用的参数、局部变量、中间结果……)。
栈帧包含:
- 局部变量区,(基本数据类型)
- 操作数栈,
- 帧数据区
下面使用代码说明
class Fruit {
static int x = 10;
static BigWaterMelon bigWaterMelon_1 = new BigWaterMelon(x);
int y = 20;
BigWaterMelon bigWaterMelon_2 = new BigWaterMelon(y);
public static void main(String[] args) {
final Fruit fruit = new Fruit();
int z = 30;
BigWaterMelon bigWaterMelon_3 = new BigWaterMelon(z);
new Thread() {
@Override
public void run() {
int k = 100;
setWeight(k);
}
void setWeight(int waterMelonWeight) {
fruit.bigWaterMelon_2.weight = waterMelonWeight;
}
}.start();
}
}
class BigWaterMelon {
public BigWaterMelon(int weight) {
this.weight = weight;
}
public int weight;
}
2.线程共享情况
程序计数器:作用可以看做是当前线程所执行的字节码的行号指示器
本地方法栈:为虚拟机使用到的Native方法服务
3.堆栈的区别
3.1 栈的优势存取速度比堆要快,仅次于直接位于CPU中的寄存器。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。另外,栈数据可以共享。
3.2堆的优势可以动态地分配内存大小,生存期也不必事先告诉编译器,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。
4.static,final,内部类的说明
4.1static修饰符
static修饰符能够与属性、方法和内部类一起使用,表示静态的。类中的静态变量和静态方法能够与类名一起使用,不需要创建一个类的对象来访问该类的静态成员,所以,static修饰的变量又称作“类变量”。
static属性的内存分配
一个类中,一个static变量只会有一个内存空间,虽然有多个类实例,但这些类实例中的这个static变量会共享同一个内存空间。static的变量是在类装载的时候就会被初始化,即,只要类被装载,不管是否使用了static变量,都会被初始化。
static的基本规则
- 一个类的静态方法只能访问静态属性
- 一个类的静态方法不能直接调用非静态方法
- 如访问控制权限允许,static属性和方法可以使用类名加“.”的方式调用,也可以使用实例加“.”的方式调用
- 静态方法中不存在当前对象,因而不能使用this,也不能使用super
- 静态方法不能被非静态方法覆盖
- 构造方法不允许声明为static的
静态初始器——静态块
静态初始器是一个存在与类中方法外面的静态块,仅仅在类装载的时候执行一次,通常用来初始化静态的类属性。
4.2final修饰符
在Java声明类、属性和方法时,可以使用关键字final来修饰,final所标记的成分具有终态的特征,表示最终的意思。
final的具体规则
- final标记的类不能被继承
- final标记的方法不能被子类重写
- final标记的变量(成员变量或局部变量)即成为常量,只能赋值一次
- final标记的成员变量必须在声明的同时赋值,如果在声明的时候没有赋值,那么只有一次赋值的机会,而且只能在构造方法中显式赋值,然后才能使用
- final标记的局部变量可以只声明不赋值,然后再进行一次性的赋值
- final一般用于标记那些通用性的功能、实现方式或取值不能随意被改变的成分,以避免被误用
- 如果将引用类型(即,任何类的类型)的变量标记为final,那么,该变量不能指向任何其它对象,但可以改变对象的内容,因为只有引用本身是final的。
4.3内部类
在一个类(或方法、语句块)的内部定义另一个类,后者称为内部类,有时也称为嵌套类。
内部类的特点
内部类可以体现逻辑上的从属关系,同时对于其它类可以控制内部类对外不可见等
外部类的成员变量作用域是整个外部类,包括内部类,但外部类不能访问内部类的private成员
- 逻辑上相关的类可以在一起,可以有效地实现信息隐藏
- 内部类可以直接访问外部类的成员,可以用此实现多继承
- 编译后,内部类也被编译为单独的类,名称为outclass$inclass的形式
内部类可以分为四种
- 类级:成员式,有static修饰
- 对象级:成员式,普通,无static修饰
- 本地内部类:局部式
- 匿名级:局部式
成员式内部类的基本规则
-
可以有各种修饰符,可以用4种权限、static、final、abstract定义
-
若有static限定,就为类级,否则为对象级。类级可以通过外部类直接访问,对象级需要先生成外部的对象后才能访问
-
内外部类不能同名
-
非静态内部类中不能声明任何static成员
-
内部类可以互相调用
成员式内部类的访问
- 内部类访问外层类对象的成员时,语法为:外层类名.this.属性
- 使用内部类时,由外部类对象加“.new”操作符调用内部类的构造方法,创建内部类的对象。
- 在另一个外部类中使用非静态内部类中定义的方法时,要先创建外部类的对象,再创建与外部类相关的内部类的对象,再调用内部类的方法。
- static内部类相当于其外部类的static成分,它的对象与外部类对象间不存在依赖关系,因此可以直接创建。
- 由于内部类可以直接访问其外部类的成分,因此,当内部类与其外部类中存在同名属性或方法时,也将导致命名冲突。所以,在多层调用时要指明。
本地类
定义在代码块中的类,只在定义它们的代码块中可见。
本地类有以下几个重要特性:
-
仅在定义了它们的代码块中可见
-
可以使用定义它们的代码块中的任何本地final变量(注:本地类(也可以是局部内部类/匿名内部类等等)使外部类的变量,原意是希望这个变量在本地类中的对象和在外部类中的这个变量对象是一致的,但如果这个变量不是final定义,它有可能在外部被修改,从而导致内外部类的变量对象状态不一致,因此,这类变量必须在外部类中加final前缀定义)
-
本地类不可以是static的,里边也不能定义static成员
-
本地类不可以用public、private、protected修饰,只能使用缺省的
-
本地类可以是abstract的
匿名内部类
是本地内部类的一种特殊形式,即,没有类名的内部类,而且具体的类实现会写在这个内部类里。
匿名类的规则
- 匿名类没有构造方法
- 匿名类不能定义静态的成员
- 匿名类不能用4种权限、static、final、abstract修饰
- 只可以创建一个匿名类实例