当前位置: 首页 > 科技观察

JVM调优总结:一些概念

时间:2023-03-12 08:45:25 科技观察

数据类型在Java虚拟机中,数据类型可以分为两大类:基本类型和引用类型。基本类型的变量持有的是原始值,即他所代表的值就是值本身;引用类型的变量保存引用值。“引用值”表示对对象的引用,而不是对象本身,对象本身存储在引用值所指示的地址处。基本类型包括:byte、short、int、long、char、float、double、Boolean、returnAddress引用类型包括:类类型、接口类型和数组。堆和栈堆和栈是程序运行的关键,有必要弄清楚它们的关系。栈是运行时的单位,而堆是存储的单位。栈解决的是程序的运行问题,即程序如何执行,或者说数据如何处理;堆解决了数据存储的问题,即数据怎么放,放在哪里。在Java中,一个线程会有一个线程栈与之对应,这个很好理解,因为不同的线程执行的逻辑不同,所以需要一个独立的线程栈。堆由所有线程共享。因为栈是操作的单位,所以里面存放的信息都是和当前线程(或程序)相关的。包括局部变量、程序运行状态、方法返回值等;而堆只负责存储对象信息。为什么要区分堆和栈呢?不是可以把数据存入栈吗?***,从软件设计的角度来说,栈代表处理逻辑,堆代表数据。这种分离使得处理逻辑更加清晰。分而治之的思维。这种隔离和模块化的思想体现在软件设计的方方面面。其次,堆和栈的分离使得堆的内容可以被多个栈共享(也可以理解为多个线程访问同一个对象)。这种分享的好处有很多。这种共享一方面提供了一种有效的数据交互方式(如:共享内存),另一方面堆中的共享常量和缓存可以被所有栈访问,节省了空间。第三,由于运行时的需要,需要将栈分成地址段,比如保存系统运行的上下文。由于堆栈只能向上增长,因此限制了堆栈存储内容的能力。堆是不同的。堆中的对象可以根据需要动态增长。因此,栈和堆的分裂使得动态增长成为可能。对应的栈只需要在堆中记录一个地址即可。第四,面向对象是堆和栈的完美结合。事实上,面向对象的程序和以前的结构化程序在执行上没有区别。但是,面向对象的引入改变了思考问题的方式,更接近于自然的思维方式。当我们拆解对象时,会发现对象的属性其实就是数据,存储在堆中;而对象的行为(方法)是运行逻辑并被放入栈中。当我们写一个对象的时候,我们实际上是在写数据结构和处理数据的逻辑。不得不承认面向对象设计确实很美。在Java中,Main函数是栈的起点,也是程序的起点。程序运行总是有一个起点。和C语言一样,java中的Main是起点。不管什么java程序,找到main,就会找到程序执行的入口:)堆里存的是什么?堆栈中存储了什么?对象存储在堆中。堆栈存储对堆中基本数据类型和对象的引用。一个对象的大小是不可估量的,或者是可以动态改变的,但是在栈中,一个对象只对应一个4btye的引用(栈分离的好处:))。为什么不将原始类型放入堆中?因为它占用的空间一般是1到8个字节——需要的空间较少,而且因为是基本类型,不会有动态增长——长度是固定的,所以放在栈中就够了。存在于堆中是没有意义的(也会浪费空间,后面会解释)。可以说,基本类型和对象的引用都是保存在栈中的,都是几个字节的数,所以在程序运行的时候,它们的处理方式是统一的。但是基本类型、对象引用和对象本身是不一样的,因为一个是栈中的数据,一个是堆中的数据。最常见的问题之一是Java中的参数传递问题。Java传参时传值怎么办?还是通过引用传递?要解释这个问题,首先要明确两点:1.不要试图类比C,Java中没有指针的概念2.程序总是运行在栈上,所以传递参数时,有只是基本类型和对象引用的问题。对象本身不是直接传递的。弄清楚以上两点后。Java在方法调用中传递参数时,因为没有指针,所以总是传值调用(这一点可以参考C的传值调用)。所以很多书上都说Java是传值调用,这没有问题,也简化了C的复杂度。但是引用传递的错觉是怎么造成的呢?在运行栈中,基本类型和引用的处理是一样的,都是按值传递。因此,如果是按引用调用的方法,也可以理解为“按引用值”的按值调用,即对引用的处理与基本类型完全相同。但是当进入被调用的方法时,传递的引用的值被程序解释(或查找)到堆中的对象,然后才对应到真正的对象。如果此时进行修改,修改的是引用对应的对象,而不是引用本身,即修改的是堆中的数据。所以这个修改可以保持。从某种意义上说,对象是由原始类型组成的。一个对象可以看作是一棵树。如果对象的属性还是一个对象,那么它还是一棵树(即非叶子节点),基本类型就是树的叶子节点。传递程序参数时,不能修改传递的值本身。但是,如果该值为非叶子节点(即对象引用),则可以修改该节点下的所有内容。在堆和栈中,栈是程序运行最基础的东西。程序可以在没有堆的情况下运行,但不能没有堆栈。堆是栈的数据存储服务。说白了,堆就是一块共享内存。但是,也正是因为有了堆栈分离的思想,Java的垃圾回收才成为可能。在Java中,堆栈的大小由-Xss设置。当栈中存储的数据较多时,需要适当增大这个值,否则会出现java.lang.StackOverflowError。这种异常常见的情况是递归无法返回,因为此时栈中保存的信息就是方法返回的记录点。Java对象的大小基本数据类型的大小是固定的,这里就不多说了。对于非基本类型的Java对象,它们的大小是有争议的。在Java中,一个空的Object对象的大小是8byte,正好是堆中一个没有任何属性的对象的大小。看下面的语句:Objectob=newObject();这样,一个Java对象的生命在程序中就完成了,但是它占用的空间是:4byte+8byte。4byte是上节提到的Java栈中保存引用所需的空间。而那8byte就是Java堆中对象的信息。因为所有的非基础Java对象默认都需要继承Object对象,所以无论是哪种Java对象,其大小都必须大于8byte。有了Object对象的大小,我们就可以计算出其他对象的大小。ClassNewObject{整数;布尔标志;对象对象;}//大小为:空对象大小(8byte)+int大小(4byte)+布尔大小(1byte)+空对象引用大小(4byte)=17byte。但是因为Java是按照8的整数倍分配对象内存的,大于17byte的最接近8的整数倍是24,所以这个对象的大小是24byte。这里需要注意基本类型的包装类型的大小。因为这个包装器类型已经是一个对象,所以需要将它们当作对象来对待。封装类型的大小至少为12bytes(至少是声明一个空Object所需的空间),12bytes不包含任何有效信息。同时,由于Java对象的大小是8的整数倍,一个基本类型包装类的大小至少为16bytes。这个内存占用很恐怖,它使用了基本类型的N倍(N>2),有些类型的内存占用更夸张(想想就知道了)。因此,如果可能,应谨慎使用包装类。JDK5.0之后,因为加入了自动类型转换,Java虚拟机对存储进行了相应的优化。引用类型对象引用类型分为强引用、软引用、弱引用和幻引用。强引用:我们一般声明对象是虚拟机生成的引用。在强引用环境下,垃圾回收时需要严??格判断当前对象是否被强引用。如果是强引用,则不会被垃圾回收。软引用:softreference一般用作缓存。与强引用不同的是,当软引用被垃圾回收时,虚拟机会根据当前系统的剩余内存来决定是否回收软引用。如果剩余内存比较紧张,虚拟机会回收软引用引用的空间;如果剩余内存比较丰富,则不会回收。也就是说,当虚拟机发生OutOfMemory时,一定没有软引用存在。弱引用:弱引用类似于软引用,用作缓存。但与软引用不同的是,弱引用在垃圾回收时肯定会被回收,所以它们的生命周期只存在于一个垃圾回收周期内。强引用就不用说了,我们系统在使用的时候一般都会用到强引用。“软引用”和“弱引用”比较少见。它们一般作为缓存使用,一般在内存大小比较有限的时候作为缓存使用。因为如果内存足够大,可以直接使用强引用作为缓存,可控性更高。因此,它们通常用作桌面应用程序中的缓存。原文链接:http://pengjiaheng.iteye.com/blog/518623【编辑推荐】JavaGUI编写的画板程序,Java的动态绑定机制jOOQ2.0.2发布Java的ORM框架JavaFX2012:彻底开源Java与复选框树的实现与应用