Java中堆、栈,静态方法和非静态方法的速度问题
一、堆和栈的速度性能分析
堆和栈是JVM内存模型中的2个重要组成部分,自己很早以前也总结过堆和栈的区别,基本都是从存储内容,存储空间大小,存储速度这几个方面来理解的,但是关于堆和栈的存储速度,只知道堆存储速度慢,栈存储速度快,至于为什么堆比栈的存取速度慢,并没有特别深入的研究,从网上也找了很多资料,但很多理由并不太认同,这里也列举一些,并结合自己的理解来分析,如果不正确欢迎指正。
1、从分配的角度分析
java中栈的大小和生命周期在编译期间就确定了的(可以参考之前写的一篇JVM内存模型中的分析,本周末会写一篇该系列知识点中GC策略和GC收集器的博客),而堆是在运行时动态分配的,这会花不少时间,因此从分配的角度来说,堆比栈速度慢。
2、从访问角度分析
网上很多文章都说访问栈只需1次,而访问堆需要2次,一次取地址,第二次根据地址去访问对象,这个观点我并不是完全认同。我们知道,虚拟机栈中存储的是一个个栈帧,每个栈帧中存储的是一些局部变量表,操作数,动态链接和返回地址等,当访问栈的时候,一次访问就可以获取这些数据,而java中访问堆对象的方式主要有2种:通过直接指针和句柄访问,直接指针的方式有点类似于数组的首地址,通过直接指针能快速找到这个对象,只需1次访问。这种方式相比句柄的好处是速度更快,但缺点也很明细:当进行GC的时候,地址会发生变化,而GC是很频繁的。另一种方式是句柄,句柄就相当于一个小区的门卫,当你要找这个小区里的某个住户时(这个住户很有钱很任性,每天住在不同的楼层和房间),你要先去找门卫,门卫会告诉你这个人他今天在哪栋楼哪个房间,然后你再到这个房间去找就行了。这样一来你就需要访问2次(1次门卫,再根据门卫去找住户)。这样速度自然就慢了,但这种方式的好处就是:通过门卫你永远都能知道这个住户在哪里,不管住户怎么变(GC过程中对象会频繁移动,导致地址会频繁变更)。因此我的理解应该是:如果堆使用的是直接指针的方式的话,从访问角度来说,应该区别不大,当然如果是句柄的方式,倒有些道理。
3、从CPU命中率角度分析
我们知道CPU有3级缓存,一级缓存速度最快,接近CPU的速度,但是一级缓存比较小,二级缓存速度次之,空间稍大,三级缓存速度又慢些,空间又大些,而且CPU读取的时候是按行来读取的,比如64位的机器每次读取的就是64位,相当于每次可以读取2个int类型的长度,每次读取某个数据的时候,可能会把相邻的数据一块读取进来,而栈占用的空间小,这样CPU的命中率会更高些,而且淘汰率会更低,而堆占用的空间大,相对来说,每次读取命中率更低了,淘汰率也更高,因此从这个角度来说,栈也比堆要快写。
上面说的是堆和栈的存储速度区别,下面再来分析下静态方法和非静态方法的速度比较。
二、静态方法和非静态方法(已经创建对象前提下)执行性能分析
其实之前的直觉是静态方法的访问速度应该会比非静态方法快,因为静态方法在加载类的时候就存到方法区了,运行时可以直接调用,而非静态方法调用时需要先初始化对象再来调用,那问题来了:假如对象已经初始化了,再调用静态方法和非静态方法哪个快呢?开始以为非静态方法要快,因为非静态方法是存储在虚拟机栈中的,而栈的访问速度是比较快的,但是这并不严谨,那就来个实验吧。
下图是多次运行的结果:
第一次:
第二次:
第三次:
第四次:
可以看到,循环10000次的结果里,非静态方法的执行速度4次里有3次都比静态方法快。再来个100000次的循环看看结果:
第一次:
第二次:
第三次:
第4次:
这个就更明显了,所以就实验结果而言,如果在已经创建对象的前提下,非静态方法的访问速度是比静态方法的访问速度快的。但是至于原因,上面的理由感觉还是有点勉强,依旧不是很清楚,欢迎各位大神指点
Java中堆、栈,静态方法和非静态方法的速度问题的更多相关文章
- Java中 堆 栈,常量池等概念解析(转载)
1.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制. 2. 栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(字符 ...
- java中静态方法和非静态方法调用的一点小困扰,已解决。
public static void main(String[] args) { // TODO Auto-generated method stub SimpleGui1B gui=new Simp ...
- Java中synchronized用在静态方法和非静态方法上面的区别
synchronized 修饰在 static方法和非static方法的区别 在Java中,synchronized是用来表示同步的,我们可以synchronized来修饰一个方法.也可以sync ...
- 在java中静态方法与非静态方法
在java中public void与public static void有什么区别 ? public void 修饰是非静态方法,该类方法属于对象,在对象初始化(new Object())后才能被调用 ...
- Java中堆内存和栈内存详解2
Java中堆内存和栈内存详解 Java把内存分成两种,一种叫做栈内存,一种叫做堆内存 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个变量时,ja ...
- Java中堆和栈的区别(转)
栈与堆都是Java用来在Ram中存放数据的地方.与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆. Java的堆是一个运行时数据区,类的对象从中分配空间.这些对象通过new. ...
- 转 C#中静态方法与非静态方法区别比较
C#静态方法与非静态方法的区别不仅仅是概念上的,那么他们有什么具体的区别呢?让我们通过本文向你做一下解析. C#的类中可以包含两种方法:C#静态方法与非静态方法.那么他们的定义有什么不同呢?他们在使用 ...
- Java中堆与栈
简单的说:Java把内存划分成两种:一种是栈内存,一种是堆内存. 1:什么是堆内存: 堆内存是是Java内存中的一种,它的作用是用于存储Java中的对象和数组,当我们new一个对象或者创建一个数组的时 ...
- Java中堆(heap)和栈(stack)的区别
简单的说: Java把内存划分成两种:一种是栈内存,一种是堆内存. 在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配. 当在一段代码块定义一个变量时,Java就在栈中为这个变量分 ...
随机推荐
- 计算机为什么要区别C盘,D盘,E盘等?
为什么要区分C盘,D盘,E盘,F盘? 1)各盘出现背景 在计算机刚诞生的年代,还没有硬盘,那时数据存储主要靠软盘.软盘驱动器按照顺序占据了A和B盘符的位置,后来随着硬盘的应用,就出现了C盘及以后的 ...
- C语言实现通用链表初步(三)----单元测试
前两节,我们已经完成了链表的一些操作,快来测试一下吧. 这里使用的单元测试工具名字叫"check". START_TEST(my_slist_1) { struct student ...
- ubuntu下mysql安装(server、client、dev),开启、停止和重启,及常见错误
转自:ubuntu下mysql安装(server.client.dev),开启.停止和重启,及常见错误 1. 在ubuntu下安装server和client很简单: (1)安装server apt-g ...
- html常用样式
1.小手:style="cursor: pointer;" 2.滚动条:style="height: 1oopx;overflow: auto;"; 3.文本框 ...
- JavaScript 数组(Array)
//声明方式 //调用 Array构造函数创建并赋值 var users = new Array(); //new 可以省略 ); //new 可以省略 var users3 = new Array( ...
- IDEA 生成 JPA实体类
文章目录1.创建工程2.连接数据源3.生成`JPA`实体类4.生成实体类结果1.创建工程使用Maven来构建工程,为了简化创建步骤 创建一个新工程不包含任何Maven模板,[按需添加] 命名 Grou ...
- maven课程 项目管理利器-maven 3-6 maven中Pom.xml的解析 3星
本节主要讲了pom中常用的标签 1 常用的标签 2 非常用的标签 1 常用的标签 a 坐标 groupid 反写公司网址+项目名 artifactId 项目名+模块名 version 版本号:3位版本 ...
- vue学习第四天 ------ 临时笔记
1.驼峰写法 在html标签中,由于html的特性是不区分大小写(比如LI和li是一样的),因此,html标签中要传递的值要写成短横线式的(如btn-test),以区分大小写. 在props的数组中, ...
- MySQL(三) 完整性约束
一.介绍 约束条件与数据类型的宽度意义,都是可选参数. 作用:用于保证数据的完整性和一致性. 主要分为: PRIMARY KEY (PK) 标识该字段为该表的主键,可以唯一的标识记录 FOREIGN ...
- Android getRunningTasks和getRunningAppProcesses失效
Android 5.0以上的getRunningTasks失效,该方法可以获得在前台运行的系统进程.可以用getRunningAppProcesses方法暂时替代. android6.0以上的getR ...