JVM基础和调优(一)

最近的项目中,出现了内存和性能的问题,需要优化,所以趁着这个机会,把自己关于java虚拟机的东整理一下,不对的地方,欢迎指出。

数据类型,因为在java的优化的过程中,检测到的数据类型一般比较的基础,毕竟复杂的数据类型就是有基础的组合而来的。

Java虚拟机中,数据类型可以分为两类:基本类型和引用类型。基本类型的变量保存原始值,表示的是数据本身的值,是数据中的最基础的部分,一般包含:

byte,short,int,long,char,float,double,Boolean,returnAddress

其中的byte,short,int,long,char,float,double,Boolean 就不多说了,returnAddress 是会被Java虚拟机的jsr、ret和jsr_w指令所使用。return Address类型的值指向一条虚拟机指令的操作码。与前面的几种类型不同,returnAddress类型在Java语言之中并不存在相应的类型,也无法在程序运行期间更改returnAddress类型的值。

引用类型的变量保存引用的值,这个就是门牌,对应一个屋子,但是保存的是屋子的地址,也就是变量在内存中存储的首地址,一般包括:

类引用,接口引用,数组

堆和栈 这个比较的重要的概念,很有必要说一下。

程序运行的角度来说,一个程序能够运行起来,主要分为数据和逻辑,对应的就是堆和栈的对应关系。

栈,是程序运行时的逻辑存储的东西,比较标识方法的栈帧,表示程序运行时的单元。

堆,是程序运行时数据存储的地方,比如新建的对象,表示程序存储的单元。

栈解决程序的运行问题,即程序如何执行,或者说如何处理数据;

堆解决的是数据存储的问题,即数据怎么放、放在哪儿。

在Java中一个线程就会相应有一个线程栈与之对应,这点很容易理解,因为不同的线程执行逻辑有所不同,因此需要一个独立的线程栈。而堆则是所有线程共享的。栈因为是运行单位,因此里面存储的信息都是跟当前线程(或程序)相关信息的。包括局部变量、程序运行状态、方法返回值等等组合而成的栈帧;而堆只负责存储对象信息。

为什么这么设计?当初设计的JVM规范的那些大牛,为什么会把这两者区分开,我们只能根据自己的推测,来猜想一二了。

① 符合面向对象的理念,把变化的和和不变得分离(总感觉有太极中阴阳的感觉),我们可以知道面向对象和面向过程的程序在执行上或者说针对需求的满足上面没有什么区别,但是面向对象的引用更加的贴近自然的思考方式。我们在编写程序的时候,仔细的想想能够知道我们编写的类的成员的变量,对象的属性就是存放在堆中,但是方法就是存储在栈中,这样我们在编写对应对象的数据结构的时候,实际上就是处理了数据和对应的逻辑,实现了太极中统一。仔细想想面向对象的这种设计比较的美。

② 从软件设计的角度讲:栈代表的处理逻辑,而堆代表的是处理的数据,这样就把处理什么,和怎么处理分开了,是的处理的逻辑更加的清晰,这种模块化的,隔离的思想在软件设计的方方面面都有体现。

③ 堆和栈的分离,不仅使栈对应的线程能够更好的关注自己的处理逻辑,还能够使堆中的内容可以被对多个栈共享,可以理解多个线程访问同一个对象,这种情况提供了一种有效的数据交互的方式,比如共享数据。另一方面也节约了空间,当然线程安全也是一个问题。

④ 栈因为运行的需要,需要保存运行时的上下文,需要进行地址段的划分,栈中对数据的引用只是一个内存的首地址,所以这样的话,堆中的对象就能够根据需要动态的增长。

堆和栈分离了,那么堆中存储的是什么,栈中存储的又是什么?

堆中存的是对象,栈中存储的是基本数据类型和堆中对象的引用。一个对象的大小是不可估计的,或者说可以动态变化的,但是栈中对应的只存了一个引用,4个byte,所以说java程序理论上面有实例数的限制。

为什么把基本类型放在栈中,一个原因是因为占有的空间小,本身占用的空间就小,放在堆中有需要加上四个字节的引用,划不来,再说基本类型的的长度是固定的,不会出现动态的增长。

现在基本类型和对象的引用都是存放在栈中,而且都是几个字节的一个数,因此在逻辑处理的时候,能够保持一致,但是基本类型和对象引用,对象本身是有区别的,这个时候对应的一个基本的问题是,java中参数的传递问题:

Java中的参数传递时传值呢?还是传引用?
要说明这个问题,先要明确两点:
     1. 不要试图与C进行类比,Java中没有指针的概念。

2. 程序运行永远都是在栈中进行的,因而参数传递时,只存在传递基本类型和对象引用的问题。不会直接传对象本身。

因此java的方法的调用,都是传值调用,但是传引用调用的错觉是怎么造成的:

在运行栈中,基本类型和引用的处理是一样的,都是传值,所以,如果是传引用的方法调用,也同时可以理解为“传引用值”的传值调用,即引用的处理跟基本类型是完全一样的。但是当进入被调用方法时,被传递的这个引用的值,被程序解释(或者查找)到堆中的对象,这个时候才对应到真正的对象。如果此时进行修改,修改的是引用对应的对象,而不是引用本身,即:修改的是堆中的数据。所以这个修改是可以保持的了。

对象,从某种意义上说,是由基本类型组成的。可以把一个对象看作为一棵树,对象的属性如果还是对象,则还是一颗树(即非叶子节点),基本类型则为树的叶子节点。程序参数传递时,被传递的值本身都是不能进行修改的,但是,如果这个值是一个非叶子节点(即一个对象引用),则可以修改这个节点下面的所有内容。

这一段是摘抄自网上的,感觉说的比较的好。

java中,栈的大小可以通过-Xss来设置,当栈中存储的数据比较多时,可以适当的调大这个值,否则会出现 java.lang.stackoverflowerror的异常,常见的出现的异常是循环递归的情况。堆的大小可以通过-Xmx3/ –Xms设置,后面我们详细的说。

说完了存储的结构,再说一下一个java对象到底有多大?

基本的数据类型是固定的,就不说了,在java中,一个空的Object对象的大小是8byte,并且这个大小事保存堆中一个没有任何属性对象的大小,如果包括引用,也就是Object instance = new Object(); 就是12byte,其中的4byte就是引用所占的空间。

Class Student{

int nmber;

boolean isGood;

Object teacher;

}

大小为:8(空的Object) + 4(int的大小)+1(boolean的大小)+4(teacher引用的大小) = 17byte,因为java中对对象内存分配的时候都是以8的整数倍来分的,依次分配为24,这个对象的大小就是24byte。

另外关于引用的,就是java中引用的分类:

强引用,软引用,弱引用,虚引用

各个类别,就不详细说了,因为在优化的过程中,我们的代码一般采取的就是强引用,软引用和弱引用一般的在处理缓存的逻辑中用到。

时间: 2024-10-07 02:54:17

JVM基础和调优(一)的相关文章

JVM基础和调优(四)

垃圾回收算法中的一些问题 再上一遍中,说道JVM并不是采用一种垃圾回收的方法,因为不同的内存块采取的方法是不样的,那么:为什么要分块?为什么不采用同一种方法回收垃圾,这样不是更加的统一吗? 分块的垃圾回收策略,是基于这样一个事实:不同的对象的生命周期是不一样的.因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率. 在Java程序运行的过程中,会产生大量的对象,其中有些对象是与业务信息相关,比如Http请求中的Session对象.线程.Socket连接,这类对象跟业务直接挂钩,因此生

JVM基础和调优(六)

JVM设置过程中的一般的规范 在JVM的设置中,年轻代的设置比较的重要,因为年轻代存储空间分配的比较的块,可以说触发GC的机会比较的大. 默认的情况下:-XX:NewRatio  默认为2 说明:年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代),设置为2,则年轻代与年老代所占比值为1:2,年轻代占整个堆的1/3 -Xmx:设置JVM最大可用内存. -Xms:设置JVM最小可用内存.此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存. -Xmn:设置年

JVM基础和调优(二)

主要讲述java虚拟机的内存体系结构 了解了JVM 的一些基础之后,我们来看看java虚拟机内存的体系结构,这个是理解JVM垃圾收集算法的前提,理解了内存结构我们才能够针对不同的部分根据我们的程序进行优化.前面已经说明了,java的堆和栈,但是只是局部的说了一下,没有在java内存体系中说明. 这一节,主要来学习jvm的基本结构,也就是概述.说是概述,内容很多,而且概念量也很大,不过关于概念方面,你不用担心,我完全有信心,让概念在你的脑子里变成图形,所以只要你有耐心,仔细,认真,并发挥你的想象力

JVM基础和调优(五)

垃圾回收算法中收集器 接着上面的说,了解了JVM收集垃圾的过程,然后我们看一看收集器. 串行收集器:用单线程处理所有垃圾回收工作,因为无需多线程交互,所以效率比较高.但是,也无法使用多处理器的优势,所以此收集器适合单处理器机器.当然,此收集器也可以用在小数据量(100M左右)情况下的多处理器机器上.可以看到中间是一个直接的停顿的状态.可以使用-XX:+UseSerialGC打开. 示意图: 并行收集器: 对年轻代进行并行垃圾回收,因此可以减少垃圾回收时间.一般在多线程多处理器机器上使用.使用-X

JVM基础和调优(三)

主要讲解垃圾回收的算法 上面我们已经了解到了,JVM的体系的结构,这次我们来说一下垃圾回收的算法. 1. 最开始的想法,或者说垃圾回收的最容易想到的就是:引用计数(reference count) 我们记录每一个对象受到引用的次数,每增加一个引用就在这个对象对应的引用次数加一,每减少一次对对象的印象对应的引用次数就减一,回收的时候直接检查引用的次数,次数为零的时候直接的回收.这个有一个缺点就是循环引用的问题.但是还有程序在使用这种算法, 2. 针对每一个对象,使用引用计数不行,我们可以使用标记,

Tomcat(JVM)性能调优

Tomcat架构图 Tomcat与JVM版本优化Tomcat的运行是基于Java的虚拟机.SUN的JVM动态库有client和server两个版本,分别针对桌面应用和服务器应用做了相应的优化,client版本加载速度较快,server版本加载速度较慢但运行起来较快.在命令行输入 java -version 可以看到jvm目前配置的是哪个版本.如果要修改jvm的版本,可更改默认java.exe调用的jvm.dll,这个由jvm.cfg决定.编辑%JAVA_HOME%/jre/lib/i386/jv

Java系列笔记 - JVM监控与调优

光说不练假把式,学习Java GC机制的目的是为了实用,也就是为了在JVM出现问题时分析原因并解决之.通过学习,我觉得JVM监控与调优主要的着眼点在于如何配置.如何监控.如何优化3点上.下面就将针对这3点进行学习. (如果您对Java的内存区域划分和内存回收机制尚不明确,那在阅读本文前,请先阅读我的前一篇博客<Java系列笔记(3) - Java 内存区域和GC机制>,在该博客中,详细叙述了Java HotSpot虚拟机(Sun/Oracle JDK系列默认的虚拟机)的内存分配和垃圾回收机制.

2020年薪30W的Java程序员都要求熟悉JVM与性能调优!

前言 作为Java程序员,你有没有被JVM伤害过?面试的时候是否碰到过对JVM的灵魂拷问? 一.JVM 内存区域划分 1.程序计数器(线程私有) 程序计数器(Program Counter Register),也有称作为 PC 寄存器.保存的是程序当前执行的指令的地址(也可以说保存下一条指令的所在存储单元的地址),当 CPU 需要执行指令时,需要从程序计数器中得到当前需要执行的指令所在存储单元的地址,然后根据得到的地址获取到指令,在得到指令之后,程序计数器便自动加 1 或者根据转移指针得到下一条

Linux基础的调优及安全设置

基础的调优及安全设置. a简单优化Linux 1.关闭Selinux [[email protected] selinux]#cd /etc/selinux/ [[email protected] selinux]# sed -i s#SELINUX=enforcing#SELINUX=disabled#g config [[email protected] selinux]# cat config # This file controls the state of SELinux on the