Java的内存机制详解
Java把内存分为两种:一种是栈内存,另一种是堆内存。在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,当在一段代码块定义一个变量时,Java 就在栈中为这个变量分配内存空间,当超过变量的作用域后(比如,在函数A中调用函数B,在函数B中定义变量a,变量a的作用域只是函数B,在函数B运行以后,变量a会自动被销毁。分配给它的内存会被回收),Java会自动释放掉为该变量分配的内存空间,该内存空间可以立即另做他用。
堆内存用来存放由new创建的内存数组,在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。在堆中产生一个数组或对象之后,还可以在栈中定义一个特殊的变量,让栈中的这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就变成了数组或对象的引用变量,以后就可以在程序中使用栈中的变量来访问堆中的数组或者对象,引用变量就相当于为数组或者对象起的一个名字。引用变量是普通的变量,定义时在栈中分配,引用变量在程序运行到其他作用域之外后边释放。而数组和对象本省在堆中分配,即使程序运行到使用new产生的数组或者对象的语句所在的代码块之外,数组和对象本省占据的内存不会被释放。数组和对象在没有引用变量指向它的时候,才变为垃圾,不能再被使用,在随后的一个不确定时间被垃圾回收器收走(释放掉)。这也是Java比较占内存的原因,实际上,栈中的变量指向堆内存中的变量,这就是Java中的指针。
代码实例Demo1:单个对象创建
class Person {
String name ;
int age ;
public void tell() {
System.out.println("姓名:"+name+",年龄:"+age);
}
}
public class Demo1 {
public static void main(String[] args) {
Person per = new Person() ;
}
}
在上述程序中实例化了一个对象per,在实例化的过程中需要再内存中开辟空间,这其中就包括栈内存和堆内存,具体的内存分配如下图所示:

图1-1 对象的实例化过程
我们可以从上图中发现,对象名称per被保存在了栈内存中(更加准确的说法是,在栈内存中保存的是堆内存空间的访问地址),而对象的具体内容,比如属性name和age,被保存在堆内存中。因为per对象只是被实例化,还没有被具体赋值,所以都是默认值。字符串的默认值为null,int的类型的默认值为0。前面已经提到,堆内存空间必须使用new关键字才能开辟。
代码实例Demo2:多个对象创建
1 class Person {
2 String name ;
int age ;
public void tell() {
System.out.println("姓名:"+name+",年龄:"+age);
}
}
public class Demo2 {
public static void main(String[] args) {
Person per1 = new Person() ;
Person per2 = new Person() ;
per1.name="张三" ;
per1.age=30 ;
15 per2.age=33 ;
per1.tell();
per2.tell();
}
}

图1-2 实例化两个对象
关键概念:类跟数组一样,都是属于引用类型,引用类型就是指同一个堆内存可以被多个栈内存指向,下面来看一下引用传递的简单实例。
代码实例Demo3:对象引用传递1
class Person {
String name ;
int age ;
public void tell() {
System.out.println("姓名:"+name+",年龄:"+age);
}
}
public class Demo3 {
public static void main(String[] args) {
Person per1 = new Person() ;
Person per2 = per1 ;//-------注意--------
per1.name="张三" ;
per1.age=30 ;
per2.age=33 ;
per1.tell();
per2.tell();
}
}
程序运行结果为:

从程序的运行结果可以发现,两个对象输出的内容一样,实际上所谓的引用传递,就是将一个堆内存空间的使用权交给多个栈内存空间,每个栈内存空间都可以修改堆内存空间的内容,此程序的内存分配图如下所示:

图1-3 对象引用的传递内存分配

图1-3 对象引用的传递内存分配(续)
注意:上述实例中对象per2没有堆内存空间,这是因为对象per2只进行声明操作,也没有进行实例化操作。只是使用new关键字,实例化以后才会有堆内存空间
代码实例Demo4:对象引用传递2
class Person {
String name ;
int age ;
public void tell() {
System.out.println("姓名:"+name+",年龄:"+age);
}
}
public class Demo4 {
public static void main(String[] args) {
Person per1 = new Person() ;
Person per2 = new Person() ;
per1.name="张三" ;
per1.age=30 ;
per2.name="李四" ;
per2.age=33 ;
per2=per1 ;//-----注意----
per1.tell();
per2.tell();
}
}
上述运行程序结果为:

从程序的输出结果可以发现跟Demo3差不多。不过内存分配发生了一些变化,具体如下所示:

图1-4 (垃圾对象)的产生
注意点:
1.Java本身提供垃圾收集机制(Garbage Collection,GC),会不定期释放不用的内存空间,只要对象不用了,就会等待GC释放空间,如上面堆内存中的name="李四";age=33。
2.一个栈内存只能指向一个堆内存空间,如果要想指向其他堆内存空间,则必须先断开已有的指向,才能分配新的指向。
Java中常见的内存区域
在Java中主要存在4块内存空间,这些内存的名称及作用如下:
1.栈内存空间:保存所有对象的名称。
2.堆内存空间:保存每个对象的具体属性内容。
3.全局数据区:保存static类型的属性值。
4.全局代码区:保存所有的方法定义。
完结。。。
《成功在于坚持》
Java的内存机制详解的更多相关文章
- Linux 内存机制详解宝典
Linux 内存机制详解宝典 在linux的内存分配机制中,优先使用物理内存,当物理内存还有空闲时(还够用),不会释放其占用内存,就算占用内存的程序已经被关闭了,该程序所占用的内存用来做缓存使用,对于 ...
- Java中反射机制详解
序言 在学习java基础时,由于学的不扎实,讲的实用性不强,就觉得没用,很多重要的知识就那样一笔带过了,像这个马上要讲的反射机制一样,当时学的时候就忽略了,到后来学习的知识中,很多东西动不动就用反射, ...
- Java 动态代理机制详解
在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,依赖注入就不用多说了,而对于Spring的核心AOP来说,我们不但要知道怎么通过AOP来满足的 ...
- Java虚拟机内存分配详解
简介 了解Java虚拟机内存分布的好处 1.了解Java内存管理的细节,有助于程序员编写出性能更好的程序.比如,在新的线程创建时,JVM会为每个线程创建一个专属的栈 (stack),其栈是先进后出的数 ...
- Java动态代理机制详解(JDK 和CGLIB,Javassist,ASM)
class文件简介及加载 Java编译器编译好Java文件之后,产生.class 文件在磁盘中.这种class文件是二进制文件,内容是只有JVM虚拟机能够识别的机器码.JVM虚拟机读取字节码文件,取出 ...
- Java 动态代理机制详解(JDK 和CGLIB,Javassist,ASM)
class文件简介及加载 Java编译器编译好Java文件之后,产生.class 文件在磁盘中.这种class文件是二进制文件,内容是只有JVM虚拟机能够识别的机器码.JVM虚拟机读取字节码文件,取出 ...
- Java动态代理机制详解(类加载,JDK 和CGLIB,Javassist,ASM)
class文件简介及加载 Java编译器编译好Java文件之后,产生.class 文件在磁盘中.这种class文件是二进制文件,内容是只有JVM虚拟机能够识别的机器码.JVM虚拟机读取字节码文件,取出 ...
- Java 对象序列化机制详解
对象序列化的目标:将对象保存到磁盘中,或允许在网络中直接传输对象. 对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久的保存在磁盘上,通过网络将这种二进制流传 ...
- Java垃圾回收机制详解和调优
gc即垃圾收集机制是指jvm用于释放那些不再使用的对象所占用的内存.java语言并不要求jvm有gc,也没有规定gc如何工作.不过常用的jvm都有gc,而且大多数gc都使用类似的算法管理内存和执行收集 ...
随机推荐
- 变形transform的副作用
前面的话 变形transform本来是一个用来处理移动.旋转.缩放和倾斜等基本操作的CSS3属性,但该属性除了完成其本职工作之后,还对普通元素造成了意想不到的影响,本文将详细介绍transform ...
- Redis系列四(keepalived+lvs搭建负载均衡)
1.安装Keepalived(主备服务器都要安装) 10.8.80.218 主服务器 10.8.80.217 备服务器 10.8.80.200 虚拟IP $ wget http://www.ke ...
- C#进阶系列——使用Advanced Installer制作IIS安装包(一:配置IIS和Web.config)
前言:大过年的,写篇文章不容易,还是给自己点个赞~~年前找了下.net安装包的制作方法,发现Visual Studio自带的制作工具使用起来非常麻烦,需要单独下载安装包,并且什么激活认证等等屁事相当麻 ...
- WinForm 进程、线程
一.进程 进程是一个具有独立功能的程序关于某个数据集合的一次运行活动. 它可以申请和拥有系统资源,是一个动态的概念,是一个活动的实体. Process 类,用来操作进程. 命名空间:using Sys ...
- Xamarin开发IOS系列教程一:安装黑苹果
经过一番思想挣扎和斗争之后,最终还是选择采用Xamarin来开发跨平台移动应用,好处和优点大家可以搜索其它博文,因为家里面穷加上谈了恋爱,就不买苹果了,开发阶段在Windows上面直接搞定哈,时候不早 ...
- 警惕Dictionary和SortedDictionary的顺序陷阱
/*我们查询资料得知Dictionary的遍历顺序和添加Add时的顺序是一致的,不像 HashTable 顺序不可知;于是我要依赖Dictionary的这种顺序一致特性做一个,固定大小400长度的队列 ...
- winsshfs的快速入手
之前在公司使用mac ,并且通过mac下的osfuse和sshfs连接,直接将虚拟机的文件目录同步到了本地,并且可以进行实时操作修改,对于写项目,确实是省了很大一部分上传的精力. 于是在自己的win下 ...
- checkinstall包的使用
1. Checkinstall是个很有用的工具.当软件编译过后,Checkinstall能够帮助安装. 下面的命令是安装软件 ./configure make make install 但是用这种安装 ...
- 使用Jmeter3.1进行接口测试(包含需登录后测试的接口)
Jmeter版本为3.1,以下只针对此版本进行测试说明: 1.打开Jmeter3.1: 启动命令路径:apache-jmeter-3.1\bin\jmeter.bat 2.测试步骤: 1.测试计划-- ...
- JS特效——图片水平滚动
具体源码如下: <!doctype html> <html lang="en"> <head> <meta http-equiv=&quo ...