求这个手机壳上的鲨鱼皮软壳冲锋衣图片的高清源图片,感激不尽!

14245人阅读
Java基础(12)
& & & & 运行时数据区域
& & & & Java虚拟机在执行Java的过程中会把管理的内存划分为若干个不同的数据区域。这些区域有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,而有的区域则依赖线程的启动和结束而创建和销毁。
& & & &Java虚拟机包括下面几个运行时数据区域:
& & & & 程序计数器
& & & & 程序计数器是一块较小的区域,它的作用可以看做是当前线程所执行的字节码的行号指示器。在虚拟机的模型里,字节码指示器就是通过改变程序计数器的值来指定下一条需要执行的指令。分支,循环等基础功能就是依赖程序计数器来完成的。
& & & & 由于java虚拟机的多线程是通过轮流切换并分配处理器执行时间来完成,一个处理器同一时间只会执行一条线程中的指令。为了线程恢复后能够恢复正确的执行位置,每条线程都需要一个独立的程序计数器,以确保线程之间互不影响。所以程序计数器是“线程私有”的内存。
& & & & 如果虚拟机正在执行的是一个Java方法,则计数器指定的是字节码指令对应的地址,如果正在执行的是一个本地方法,则计数器指定问空undefined。程序计数器区域是Java虚拟机中唯一没有定义OutOfMemory异常的区域。
& & & & Java虚拟机栈
& & & & 和程序计数器一样也是线程私有的,生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候都会创建一个栈帧用于存储局部变量表,操作栈,动态链接,方法出口等信息。每一个方法被调用的过程就对应一个栈帧在虚拟机栈中从入栈到出栈的过程。
& & & & 通常所说的虚拟机运行时分为栈和堆,这里的栈指的就是虚拟机栈或者说虚拟机栈中的局部变量表部分。
& & & & 局部变量表存放了编译器可知的各种基本数据类型、对象引用和returnAddress类型(指向一条字节码指令的地址)。局部变量表所需的内存空间在编译器完成分配,当进入一个方法时这个方法需要在帧中分配多大的内存空间是完全确定的,运行期间不会改变局部变量表的大小。(64为长度的long和double会占用两个局部变量空间,其他的数据类型占用一个)
& & & & Java虚拟机栈可能出现两种类型的异常:1. 线程请求的栈深度大于虚拟机允许的栈深度,将抛出StackOverflowError。2.虚拟机栈空间可以动态扩展,当动态扩展是无法申请到足够的空间时,抛出OutOfMemory异常。
& & & & 本地方法栈
& & & & 本地方法栈和虚拟机栈基本类似,只不过Java虚拟机栈执行的是Java代码(字节码),本地方法栈中执行的是本地方法的服务。本地方法栈中也会抛出StackOverflowError和OutOfMemory异常。
& & & & 堆
& & & & 堆是Java虚拟机所管理的内存中最大的一块。堆是所有线程共享的一块区域,在虚拟机启动时创建。堆的唯一目的是存放对象实例,几乎所有的对象实例都在这里分配,不过随着JIT编译器的发展和逃逸技术的成熟,栈上分配和标量替换技术使得这种情况发生着微妙的变化,对上分配正变得不那么绝对。
附:在Java编程语言和环境中,即时编译器(JIT compiler,just-in-time compiler)是一个把Java的字节码(包括需要被解释的指令的程序)转换成可以直接发送给处理器的指令的程序。当你写好一个Java程序后,源语言的语句将由Java编译器编译成字节码,而不是编译成与某个特定的处理器硬件平台对应的指令代码(比如,Intel的Pentium微处理器或IBM的System/390处理器)。字节码是可以发送给任何平台并且能在那个平台上运行的独立于平台的代码。
& & & & Java堆是垃圾收集器管理的主要区域,所以也称为“GC堆”。由于现在的垃圾收集器基本上都是采用分代收集算法,所以Java堆还可细分为:新生代和老生代。在细致一点可分为Eden空间,From Survivor空间,To Survivor空间。如果从内存分配的角度看,线程共享的Java堆可划分出多个线程私有的分配缓冲区。不过无论如何划分,都与存放内容无关,无论哪个区域,都是用来存放对象实例。细分的目的是为了更好的回收内存或者更快的分配内存。
& & & & Java堆可以是物理上不连续的空间,只要逻辑上连续即可,主流的虚拟机都是按照可扩展的方式来实现的。如果当前对中没有内存完成对象实例的创建,并且不能在进行内存扩展,则会抛出OutOfMemory异常。
& & & & &方法区
& & & & &方法区也是线程共享的区域,用于存储已经被虚拟机加载的类信息,常量,静态变量和即时编译器(JIT)编译后的代码等数据。Java虚拟机把方法区描述为堆的一个逻辑分区,不过方法区有一个别名Non-Heap(非堆),用于区别于Java堆区。
& & & & &Java虚拟机规范对这个区域的限制也非常宽松,除了可以是物理不连续的空间外,也允许固定大小和扩展性,还可以不实现垃圾收集。相对而言,垃圾收集行为在这个区域是比较少出现的(所以常量和静态变量的定义要多注意)。方法区的内存收集还是会出现,不过这个区域的内存收集主要是针对常量池的回收和对类型的卸载。
& & & & 一般来说方法区的内存回收比较难以令人满意。当方法区无法满足内存分配需求时将抛出OutOfMemoryError异常。
& & & &运行时常量池
& & & &运行时常量池是方法区的一部分,Class文件中除了有类的版本,字段,方法,接口等信息以外,还有一项信息是常量池用于存储编译器生成的各种字面量和符号引用,这部分信息将在类加载后存放到方法区的运行时常量池中。Java虚拟机对类的每一部分(包括常量池)都有严格的规定,每个字节用于存储哪种数据都必须有规范上的要求,这样才能够被虚拟机认可,装载和执行。一般来说,除了保存Class文件中描述的符号引用外,还会把翻译出来的直接引用也存储在运行时常量池中。
& & & & 运行时常量池相对于Class文件常量池的另外一个重要特征是具备动态性,Java虚拟机并不要求常量只能在编译期产生,也就是并非预置入Class文件常量池的内容才能进入方法区的运行时常量池中,运行期间也可将新的常量放入常量池中。
& & & &常量池是方法区的一部分,所以受到内存的限制,当无法申请到足够内存时会抛出OutOfMemoryError异常。
& & & &对象访问
& & & &对象访问在Java语言中无处不在,即使是最简单的访问,也会涉及到Java栈,java堆,方法区这三个最重要的内存区域之间的关联关系。如下面的代码:
& & & &Object obj = new Object();
& & & &假设这段代码出现在方法体中,那么“Object obj”部分的语义将会反映到Java栈的本地变量表中,作为一个reference类型的数据存在。而“new Object();”部分的语义将会反应到Java堆中,形成一块存储Object类型所有实例数据值(Instance Data)的结构化内存,根据具体类型以及虚拟机实现的对象分布的不同,这块内存的长度是不固定的。另外,在JAVA堆中还必须包含能查找到此对象内存数据的地址信息,这些类型数据则存储在方法区中。
& & & &由于reference类型在Java虚拟机中之规定了指向对象的引用,并没有规定这个引用要通过哪种方式去定位,以及访问到Java堆中的对象的具体位置,因此虚拟机实现的对象访问方式会有所不同。主流的访问方式有两种:句柄访问方式和直接指针。
& & & &1. 如果使用句柄访问方式,Java堆中将会划分出一块内存来作为句柄池,reference中存储的就是对象的地址,而句柄中包含了对象实例数据和类型数据各自的具体地址信息。
& & & &2. 如果通过直接指针方式访问,Java堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,reference中直接存储的就是对象的地址。
& & & & 两种方式各有优势,局并访问方式最大的好处是reference中存放的是稳定的句柄地址,在对象被移动时,只会改变句柄中的实例数据指针,而reference本身不需要被修改。而指针访问的最大优势是速度快,它节省了一次指针定位的开销,由于对象访问在Java中非常频繁,一次这类开销积少成多后也是一项非常可观的成本。
& & & & 具体的访问方式都是有虚拟机指定的,虚拟机Sun HotSpot使用的是直接指针方式,不过从整个软件开发的范围来看,各种语言和框架使用句柄访问方式的情况十分常见。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:265076次
积分:3213
积分:3213
排名:第8944名
原创:62篇
转载:101篇
译文:17篇
评论:32条
(1)(5)(3)(2)(13)(3)(5)(60)(10)(4)(11)(1)(14)(5)(31)(12)中文(简体)
中文(繁體)
中文(台灣)
中文(新加坡)
中文(香港)
查看你还有多少剩余可用的内存(RAM)。按下Win+Pause打开系统窗口。查看系统已安装的内存容量。这会让你了解大概给Minecraft分配多少内存。
如果你没有很多内存(RAM),那么你可以考虑安装更多内存。这是提高计算机性能的简便方法之一。
安装最新版本的Java SE。这能在Java运行平台上保证你给Minecraft分配更多的内存。查看相关指南了解下载和安装最新版本的所有细节。
确保你下载正确的软件版本,使其适用于你的操作系统(32位或64位)。如果你想要查看你运行的是32位还是64位的Windows系统,请查阅相关指南。
打开Minecraft启动器。在1.6.X及更新版本中,你可以直接从Minecraft的启动器中直接分配更多的内存。如果你使用的是旧版本,请查阅下一部分。
选择你的资料文件。使用下拉菜单选择资料文件。点击编辑资料。
启用JVM参数。在“Java设置(高级)”部分,勾选“JVM参数”对话框。这允许你输入命令来修改Minecraft程序。
分配更多内存(RAM)。默认情况下,Minecraft给自己分配了1 GB的内存。你可以输入 -Xmx#G来增加内存量。用数字代替命令中的#来指明你希望增加分配多少千兆。例如,如果你想分配18 GB,你可以输入-Xmx3G。
确保为你的操作系统留存部分内存。例如,如果你安装的内存有20 GB,那么,你最多给Minecraft分配7 GB。
保存你的资料文件。点击保存资料来保存你的设置。
运行Minecraft。打开游戏,此时它会使用你设置的新内存来运行游戏。
查看所用设置。游戏中,按下F3来打开控制台。找到“分配的内存:”一栏,来查看分配给Minecraft的内存量。
查看你还有多少剩余可用的内存(RAM)。按下Win+Pause打开系统窗口。查看系统已安装的内存容量。这会让你了解大概给Minecraft分配多少内存。
如果你没有足够的内存(RAM),那么你可以考虑安装更多内存。这是提高计算机性能的简便方法之一。
安装最新版本的Java SE。这能在Java运行平台上保证你给Minecraft分配更多的内存。查看相关指南了解下载和安装最新版本的所有细节。
确保你下载正确的软件版本,使其适用于你的操作系统(32位或64位)。如果你想要查看你运行的是32位还是64位的Windows系统,请查阅相关指南。
打开Minecraft游戏目录。这是包含Minecraft.exe游戏文件的文件夹,你可以从这里打开Minecraft游戏。一般来说,这个文件夹不是包含minecraft.jar文件的文件夹。
右键点击并选择新建 → 文本文档。这会在和minecraft.exe文件相同的路径中创建一个新的文本文档。将文件命名为“Minecraft新启动器”或其它类似名称。
输入代码来分配更多内存。在文本文档中输入以下代码:
将####更改为你想要分配的兆字节数。如果你想要分配2 GB,就输入2048。如果你想要分配3 GB,输入3072。如果想要分配4 GB,就输入 4096。如果想要分配5 GB,就输入5120。
将文件保存为BAT文件。点击文件,选择“另存为”。将文件扩展名从.TXT改为.BAT。
如果你看不到文件扩展名,可以查阅相关指南来了解如何显示文件扩展名。
运行新文件来启动Minecraft。BAT文件会创建新的Minecraft游戏的启动器。打开.BAT文件会给游戏分配新的内存量。
查看所用设置。游戏中,按下F4来打开控制台。找到“分配的内存:”一栏,来查看分配给Minecraft的内存量。
查看你还有多少剩余可用的内存(RAM)。查看系统已安装的内存容量。这会让你了解大概给Minecraft分配多少内存。查看相关指南了解如何查看服务器上已安装的内存量。
如果你没有足够的内存(RAM),那么你可以考虑安装更多内存。这是提高计算机性能的简便方法之一。
查看你的操作系统运行的字节数。如果你运行的是32位操作系统,那么你只能给服务器分配1 GB。如果你运行64位操作系统并安装64位版本的Java,那么分配内存的限量就取决于你安装的内存卡大小。如果你想要查看你运行的是32位还是64位的Windows系统,请查阅相关指南。
打开Minecraft服务器目录。这是包含Minecraft.exe游戏文件的文件夹,你可以从这里打开Minecraft游戏服务器。
右键点击并选择新建 → 文本文档。这会在和minecraft.exe文件相同的路径中创建一个新的文本文档。将文件命名为“Minecraft新启动器”或其它类似名称。
输入代码来分配更多内存。根据你使用的操作系统在文本文档中输入以下代码:Windows
java -Xmx####M -Xms####M -exe Minecraft_Server.exe -o true
#! /bin/bash
cd "$( dirname "$0" )"
java -Xms####M -Xmx####M -exe Minecraft_Server.exe -o true
BINDIR=$(dirname "$(readlink -fn "$0")")
cd "$BINDIR"
java -Xms####M -Xmx####M -exe Minecraft_Server.exe -o true
将####更改为你想要分配的兆字节数。如果你想要分配2 GB,就输入2048。如果你想要分配3 GB,输入3072。如果想要分配4 GB,就输入 4096。如果想要分配5 GB,就输入5120。
将文件保存为BAT文件。点击文件,选择“另存为”。将文件扩展名从.TXT改为.BAT。如果你使用的是OS X,那么将文件保存为“.command”文件。如果你运行的是Linux,那么将文件保存为“.sh”文件。
如果你看不到文件扩展名,可以查阅相关指南来了解如何显示文件扩展名。
运行新文件来启动Minecraft。你创建的文件就是Minecraft服务器的新启动器文件。打开.bat、.command或.sh文件将重新给你的服务器分配新的内存量。
注意你不能给Minecraft分配多于电脑上限的内存。否则你会发现不能启动Java虚拟机,也不能运行Minecraft。
本页面已经被访问过36,598次。java中使用堆外内存,关于内存回收需要注意的事和没有解决的遗留问题(等大神解答)
JVM可以使用的内存分外2种:堆内存和堆外内存,堆内存完全由JVM负责分配和释放,如果程序没有缺陷代码导致内存泄露,那么就不会遇到java.lang.OutOfMemoryError这个错误。使用堆外内存,就是为了能直接分配和释放内存,提高效率。JDK5.0之后,代码中能直接操作本地内存的方式有2种:使用未公开的Unsafe和NIO包下ByteBuffer。
关于Unsafe对象的简介和获取方式,可以参考我的另一篇博客
java获取Unsafe类的实例和取消eclipse编译的错误
使用ByteBuffer分配本地内存则非常简单,直接ByteBuffer.allocateDirect(10 * 1024 * 1024)即可。
C语言的内存分配和释放函数malloc/free,必须要一一对应,否则就会出现内存泄露或者是野指针的非法访问。java中我们需要手动释放获取的堆外内存吗?
1、首先我们看下NIO中提供的ByteBuffer
import java.nio.ByteB
public class TestDirectByteBuffer
// -XX:MaxDirectMemorySize=40M
public static void main(String[] args) throws Exception
while (true)
ByteBuffer buffer = ByteBuffer.allocateDirect(10 * 1024 * 1024); }}}
我们将最大堆外内存设置成40M,运行这段代码会发现:程序可以一直运行下去,不会报OutOfMemoryError。如果使用了-verbose:gc -XX:+PrintGCDetails,会发现程序频繁的进行垃圾回收活动。于是我们可以得出结论:ByteBuffer.allocateDirect分配的堆外内存不需要我们手动释放,而且ByteBuffer中也没有提供手动释放的API。也即是说,使用ByteBuffer不用担心堆外内存的释放问题,除非堆内存中的ByteBuffer对象由于错误编码而出现内存泄露。
2、接下来我们看下直接在方法体中使用Unsafe的效果
import sun.misc.U
public class TestUnsafeMemo
// -XX:MaxDirectMemorySize=40M
public static void main(String[] args) throws Exception
Unsafe unsafe = GetUsafeInstance.getUnsafeInstance();
while (true)
long pointer = unsafe.allocateMemory(1024 * 1024 * 20);
System.out.println(unsafe.getByte(pointer + 1));
// 如果不释放内存,运行一段时间会报错java.lang.OutOfMemoryError
// unsafe.freeMemory(pointer);
}这段程序会报OutOfMemoryError错误,也就是说allocateMemory和freeMemory,相当于C语音中的malloc和free,必须手动释放分配的内存。
3、类似于ByteBuffer,将Unsafe分配内存封装到一个类中
import sun.misc.U
public class ObjectInHeap
private long address = 0;
private Unsafe unsafe = GetUsafeInstance.getUnsafeInstance();
public ObjectInHeap()
address = unsafe.allocateMemory(2 * 1024 * 1024);
// Exception in thread "main" java.lang.OutOfMemoryError
public static void main(String[] args)
while (true)
ObjectInHeap heap = new ObjectInHeap();
System.out.println("memory address=" + heap.address);
这段代码会抛出OutOfMemoryError。这是因为ObjectInHeap对象是在堆内存中分配的,当该对象被垃圾回收的时候,并不会释放堆外内存,因为使用Unsafe获取的堆外内存,必须由程序显示的释放,JVM不会帮助我们做这件事情。由此可见,使用Unsafe是有风险的,很容易导致内存泄露。
4、正确释放Unsafe分配的堆外内存
虽然第3种情况的ObjectInHeap存在内存泄露,但是这个类的设计是合理的,它很好的封装了直接内存,这个类的调用者感受不到直接内存的存在。那怎么解决ObjectInHeap中的内存泄露问题呢?可以覆写Object.finalize(),当堆中的对象即将被垃圾回收器释放的时候,会调用该对象的finalize。由于JVM只会帮助我们管理内存资源,不会帮助我们管理连接,文件句柄等资源,所以我们需要在finalize自己释放资源。
import sun.misc.U
public class RevisedObjectInHeap
private long address = 0;
private Unsafe unsafe = GetUsafeInstance.getUnsafeInstance();
// 让对象占用堆内存,触发[Full GC
private byte[] bytes =
public RevisedObjectInHeap()
address = unsafe.allocateMemory(2 * 1024 * 1024);
bytes = new byte[1024 * 1024];
protected void finalize() throws Throwable
super.finalize();
System.out.println("finalize." + bytes.length);
unsafe.freeMemory(address);
public static void main(String[] args)
while (true)
RevisedObjectInHeap heap = new RevisedObjectInHeap();
System.out.println("memory address=" + heap.address);
我们覆盖了finalize方法,手动释放分配的堆外内存。如果堆中的对象被回收,那么相应的也会释放占用的堆外内存。这里有一点需要注意下:
// 让对象占用堆内存,触发[Full GC
private byte[] bytes =
这行代码主要目的是为了触发堆内存的垃圾回收行为,顺带执行对象的finalize释放堆外内存。如果没有这行代码或者是分配的字节数组比较小,程序运行一段时间后还是会报OutOfMemoryError。这是因为每当创建1个RevisedObjectInHeap对象的时候,占用的堆内存很小(就几十个字节左右),但是却需要占用2M的堆外内存。这样堆内存还很充足(这种情况下不会执行堆内存的垃圾回收),但是堆外内存已经不足,所以就不会报OutOfMemoryError。
虽然改进后的RevisedObjectInHeap不会有堆外内存泄露,但是这种解决方法却无端地浪费了堆内存。简单的看了下ByteBuffer的,它内部分配堆外内存也是通过unsafe.allocateMemory()实现的。那ByteBuffer又是怎么实现的堆外内存释放呢?难道也是通过第4种类似RevisedObjectInHeap的做法吗?欢迎大神指点迷津啊!

我要回帖

更多关于 苹果7手机壳鲨鱼 的文章

 

随机推荐