-
Java栈
Java栈的区域很小, 只有1M,特点是存取速度快,所以在stack中存放的都是快速执行的任务, 基本数据类型的数据, 和对象的应用(reference)
驻留于常规RAM(随机访问存储器)区域,但可以通过它的”栈指针“获取处理的直接支持。 栈指针若向下移, 会创建新的内存; 若向上移, 会释放那些内存, 这是一种特别快、特别有效的数据保存方式, 仅次于寄存器。创建程序时, Java编译器必须准确地知道堆栈内保存的所有数据的”长度“以及“存在时间”。 这是由于它必须生成相应的代码, 以便向上和向下移动指针。这一限制无疑影响了程序的灵活性,所以尽管有些Java数据要保存在栈里——特别是对象句柄,但Java对象并不放到其中。
JVM只会直接对JavaStack(Java栈)执行两种操作
-
以帧为单位的压栈或出栈;
-
通过-Xss来设置, 若不够会抛出StackOverflowError异常。
- 每个线程包含一个栈区,栈中只保存基本数据类型的数据和自定义对象的引用(不是对象),对象都存放在堆区中
- 每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。
- 栈分为3个部分:基本数据类型的变量区、执行环境上下文、操作指令区(存放操作指令)。
栈是存放线程调用方法时存储局部变量表,操作,方法出口等与方法执行相关的信息,Java栈所占内存的大小由Xss来调节,方法调用层次太多会撑爆这个区域。
栈比较小, 所以只能存放引用, 而将数据存放在堆中
-
-
PC 寄存器
每个线程启动的时候,都会创建一个PC(Program Counter,程序计数器)寄存器。PC寄存器里保存有当前正在执行的JVM指令的地址。 每一个线程都有它自己的PC寄存器,也是该线程启动时创建的。保存下一条将要执行的指令地址的寄存器是 :PC寄存器。PC寄存器的内容总是指向下一条将被执行指令的地址,这里的地址可以是一个本地指针,也可以是在方法区中相对应于该方法起始指令的偏移量。
-
本地方法栈: 存放地址
Nativemethodstack(本地方法栈):保存native方法进入区域的地址。
-
Java堆:
存放成员变量, 存放的都是数据实体,类的对象放在heap(堆)中,所有的类对象都是通过new方法创建,创建后,在stack(栈)会创建类对象的引用(内存地址)。
类的对象放在heap(堆)中,所有的类对象都是通过new方法创建,创建后,在stack(栈)会创建类对象的引用(内存地址)。
一种常规用途的内存池(也在RAM(随机存取存储器 )区域),其中保存了Java对象。和栈不同:“内存堆”或“堆”最吸引人的地方在于编译器不必知道要从堆里分配多少存储空间,也不必知道存储的数据要在堆里停留多长的时间。因此,用堆保存数据时会得到更大的灵活性。要求创建一个对象时,只需用new命令编辑相应的代码即可。执行这些代码时,会在堆里自动进行数据的保存。当然,为达到这种灵活性,必然会付出一定的代价:在堆里分配存储空间时会花掉更长的时间
JVM将所有对象的实例(即用new创建的对象)(对应于对象的引用(引用就是内存地址))的内存都分配在堆上,堆所占内存的大小由-Xmx指令和-Xms指令来调节
-
方法区, 又叫做静态区 :存放静态变量
存放所有的①类(class),②静态变量(static变量),③静态方法,④常量和⑤成员方法。
-
跟堆一样,被所有的线程共享。
-
方法区中存放的都是在整个程序中永远唯一的元素。这也是方法区被所有的线程共享的原因。
静态变量和常量的区别: 静态变量本质是变量,是整个类所有对象共享的一个变量,其值一旦改变对这个类的所有对象都有影响;常量一旦赋值后不能修改其引用,其中基本数据类型的常量不能修改其值。
-
-
运行常量池
这儿的“静态”是指“位于固定位置”。程序运行期间,静态存储的数据将随时等候调用。可用static关键字指出一个对象的特定元素是静态的。但Java对象本身永远都不会置入静态存储空间。
这个区域属于方法区。该区域存放类和接口的常量,除此之外,它还存放成员变量和成员方法的所有引用。当一个成员变量或者成员方法被引用的时候,JVM就通过运行常量池中的这些引用来查找成员变量和成员方法在内存中的的实际地址。
在JVM中的一个进程中,每个线程都会拥有一个方法调用栈,用来跟踪线程运行中一系列的方法调用过程,栈中的每一个元素被称为栈帧,每当线程调用一个方法的时候就会向方法栈中压入一个新栈帧。这里的帧用来存储方法的参数、局部变量和运算过程中的临时数据
静态变量先存在, 早于普通成员变量(非静态变量)
static, 静态成员的特点: 访问不需要实例化对象, 类名直接可以调用, 静态函数中没有this
静态使用的注意事项:
- 静态方法只能访问静态成员(非静态既可以访问静态, 也可以访问非静态)
- 静态方法中不可以使用this或者super关键字
- 主函数是静态的
-
静态变量
当分析对象中所具备的成员的值都是相同的, 这是这个成员就可以被定义为静态的, 共享, 只要数据在对象中都是不同的, 就是对象特有数据, 必须存储在对象中, 是非静态的, 若果是相同的数据, 对象不需要修改, 只需存使用即可, 不需要存储在对象中, 定义成静态的
-
静态函数
函数是否用静态修饰, 就参考一点, 就是该函数功能是否有访问对象中的特有数据, 简单点说, 从源代码看, 该功能是否需要访问非静态的成员变量, 如果需要, 该功能就是非静态的, 如果不需要, 就可以将该功能定义成静态的。当然, 可以定义成非静态的, 但是非静态需要被对象调用, 而仅创建对象调用非静态的,没有访问特有数据的方法, 该对象的创建是没有意义的。
-
定义成静态是节约堆内存
随着类的加载而加载, 类一旦加载, 静态代码块就执行了, 而且只执行一次
作用: 用于给类进行初始化
class StaticCode{
static int num;
static{
num = 10;
num *= 3;
System.out.println("haha");
}
StaticCode(){}
static void show(){
System.out.println(num);
}
}
class StaticCodeDemo{
static{
System.out.println("a");
}
public static void main(String[] args) {
StaticCode.show();
System.out.println("b");
}
}
跟对象有关系, 有几个对象, 就在各自的对象里执行几次, 可以给所有对象初始化
// 类内
{
}
构造函数是给对应的对象进行针对性的初始化
- 静态代码块先执行
- 如果有对象, 构造代码块跟着执行
- 第三个才是构造函数