vc++64位系统下long的长度为4个字节_新来的妹子把几百万数据放入了内存,系统立马爆了...
点击上方蓝色字体,选择“标星公众号”
优质文章,第一时间送达
上一篇:这300G的Java资料是我师傅当年给我的,免费分享给大家
下一篇:这200G的Java实战资料是我师傅当年教我的第
作者:不学无数的程序员
my.oschina.net/u/4030990/blog/3145683
在公司有一个需求是要核对一批数据,之前的做法是直接用SQL各种复杂操作给怼出来的,不仅时间慢,而且后期也不好维护,就算原作者来了过一个月估计也忘了SQL什么意思了,于是有一次我就想着问一下之前做这个需求的人为什么不将这些数据查出来后在内存里面做筛选呢?直接说了你不怕把内存给撑爆吗?此核算服务器是单独的服务器,配置是四核八G的,配置堆的大小是4G。本着怀疑的精神,就想要弄清楚几百万条数据真的放入内存的话会占用多少内存呢?
计算机的存储单位
计算机的存储单位常用的有bit
、Byte
、KB
、MB
、GB
、TB
后面还有但是我们基本上用不上就不说了,我们经常将bit
称之为比特或者位、将Byte
简称为B
或者字节,将KB
简称为K
,将MB
称之为M或者兆,将GB
简称为G
。那么他们的换算单位是怎样的呢?
换算关系
首先我们得知道在计算机中所有数据都是由0 1
来组成的,那么存储0 1
这些二进制数据是由什么存放呢?就是由bit
存放的,一个bit
存放一位二进制数字。所以bit
是计算机最小的单位。
大部分计算机目前都是使用8位的块,就是我们上面称之为的字节Byte
,来作为计算机容量的基本单位。所以我们一般称一个字符或者一个数字都是称之为占用了多少字节。
了解了上面关于位和字节的关系后,我们可以看一下其他的单位换算关系
1B(Byte 字节) = 8bit(位)1KB = 1024B1MB = 1024KB1GB = 1024MB1TB = 1024GB
Java中对象占用多少内存
在了解了上面的换算关系后,我们来了解一下新建一个Java对象需要多少内存。
Java基本类型
我们知道Java类型分为基本类型和引用类型,八大基本类型有int、short、long、byte、float、double、boolean、char
数据类型 | 占用内存(单位为Byte) |
---|---|
boolean | 1 |
byte | 1 |
short | 2 |
char | 2 |
int | 4 |
float | 4 |
long | 8 |
double | 8 |
至于为什么Java中的char无论是中英文数字都占用两个字节,是因为Java中使用Unicode字符,所有的字符均以两个字节存储。
Java引用类型
在一个对象中除了有基本数据类型以外,我们也会有一些引用类型,引用类型的对象比较特殊,因为这些对象真正存储在虚拟机中的堆内存中,对象中只是存储了一个引用而已,如果是引用类型那么就会存储一个指向该引用的指针。指针默认情况下是占用4字节,是因为开启了指针压缩,如果没有开的话,那么一个引用就占用8个字节。
对象在内存中的布局
在HotSpot虚拟机中,对象在内存中存储的布局可以分为三个区域:对象头(Header)、实例数据(Instance Data)、对齐填充(Padding)。
对象头
在对象头中存储了两部分数据
运行时数据:存储了对象自身运行时的数据,例如哈希码、GC分代的年龄、锁状态标志、线程持有的锁、偏向线程ID等等。这部分数据在32位和64位的虚拟机中分别为32bit和64bit
类型指针:对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。如果对象是一个Java数组的话,那么对象头中还必须有一块用于记录数组长度的数据(占用4个字节)。所以这是一个指针,默认JVM对指针进行了压缩,用4个字节存储。
> 我们以虚拟机为64位的机器为例,那么对象头占用的内存是8(运行时数据)+4(类型指针)=12Byte。如果是数组的话那么就是16Byte
实例数据
实例数据中也拥有两部分数据,一部分是基本类型数据,一部分是引用指针。这两部分数据我们在上面已经讲了。具体占用多少内存我们需要结合具体的对象继续分析,下面我们会有具体的分析。
> 从父类中继承下来的变量也是需要进行计算的
对齐填充
对齐填充并不是必然存在的,也没有特别的含义。它仅仅起着占位符的作用。由于HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说就是对象的大小必须是8字节的整数倍。而如果对象头加上实例数据不是8的整数倍的话那么就会通过对其填充进行补全。
实战演练
我们在上面分析一大堆,那么是不是就如我们分析的一样,新建一个对象在内存中的分配大小就是如此呢?我们可以新建一个对象。
class Animal{
private int age;
}
那么怎么知道这个对象在内存中占用多少内存呢?JDK提供了一个工具jol-core
可以给我们分析出来一个对象在内存中占用的内存大小。直接在项目中引入包即可。
--Gradlecompile 'org.openjdk.jol:jol-core:0.9'
--Maven<dependency> <groupid>org.openjdk.jolgroupid> <artifactid>jol-coreartifactid> <version>0.9version>dependency>
然后我们在main函数中调用如下
public class AboutObjectMemory {
public static void main(String[] args) { System.out.print(ClassLayout.parseClass(Animal.class).toPrintable()); }}
就可以查看到输出的内容了,可以看到输出结果占用的内存是16字节,和我们分析的一样。
aboutjava.other.Animal object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 12 (object header) N/A 12 4 int Animal.age N/AInstance size: 16 bytesSpace losses: 0 bytes internal + 0 bytes external = 0 bytes total
String占用多少内存
String字符串在Java中是个特殊的存在,比如一个字符串"abcdefg"
这样一个字符串占用多少字节呢?相信会有人回答说是7个字节或者是14个字节,这两个答案都是不准确的,我们先看一下String类在内存中占用的内存是多少。我们先自己进行分析一下。在String类中有两个属性,其中对象头固定了是12字节,int是4字节,char[]数组其实在这里相当于引用对象存的,所以存的是地址,因此占用4个字节,所以大小为对象头12Byte+实例数据8Byte+填充数据4Byte=24Byte
这里的对象头和实例数据加起来不是8的倍数,所以需要填充数据进行填充。
private final char value[];
private int hash; // Default to 0
那么我们分析的到底对不对呢,我们还是用上面的工具进行分析一下。可以看到我们算出的结果和我们分析的结果是一致的。
java.lang.String object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 12 (object header) N/A 12 4 char[] String.value N/A 16 4 int String.hash N/A 20 4 (loss due to the next object alignment)Instance size: 24 bytes
那么一个空字符串占用多少内存呢?我们刚才得到的是一个String对象占用了24字节,其实char[]数组还是会占用内存的,我们在上面讲对象头的时候说过,数组对象也是一个实例对象,它的对象头比一般的对象多出来4字节,用来描述此数组的长度,所以char[]数组的对象头长度为16字节,由于此时是空字符串,所以实例数据长度为0。因此一个空char[]数组占用内存大小为对象头16Byte+实例数据0Byte=16Byte
。一个空字符串占用内存为String对象+char[]数组对象=40Byte
那么我们上面举的例子abcdefg
占用多少内存呢?其中String对象占用的内存是不会变了,变化的是char[]数组中的内容,这里我们需要知道字符串是存放于char[]数组中的,而一个byte占用2个字节,所以abcdefg
的char[]数组大小为对象头16Byte+实例数据14Byte+对齐填充2Byte=32Byte
。那么abcdefg
占用内存大小就是String对象+char[]数组对象=56Byte
用List存储对象
那么我们在内存中放入二千万个这个对象的话,需要占用多少内存呢?根据上面的知识我们能大概估算一下。我们定义一个List数组用于存放此对象,不让其回收。
List animals = new ArrayList>20000000);for (int i = 0; i > 20000000; i++) { Animal animal = new Animal(); animals.add(animal);}
> 注意这里我是直接将集合的大小初始化为了二千万的大小,所以程序在正常启动的时候占用内存是100+MB,正常程序启动仅仅占用30+MB的,所以多出来的60+MB正好是我们初始化的数组的大小。至于为什么要初始化大小的原因就是为了消除集合在扩容时对我们观察结果的影响
这里我贴一张,集合未初始化大小和初始化大小内存占用对比图,大家可以看到是有内存上的差异,在ArrayList数组中用于存放数据的是transient Object[] elementData;
Object数组,所以它里面存放的是指向对象的指针,一个指针占用4个字节,所以就有两千万个指针,那么就是76M。我们可以看到差异图和我们预想的一样。
上面我们已经算出来了一个Animal
对象占用16个字节,所以两千万个占用大概是305MB,和集合加起来就是将近380MB的空间大小,接下来我们就启动程序来看一下我们结果是不是对的呢,接下来我用的jconsole
工具查看内存占用情况。
我们可以看到和我们预算的结果是相吻合的。
那么以后如果有大量的对象需要从数据库中查找出来放入内存的话,那么如果是使用对象来接的话,那么我们就应该尽量减少对象中的字段,因为即使你不赋值,其实他也是占用着内存的,我们接下来再举个例子看一下对个属性值的话占用内存是不是又高了。我们将Animal
对象改造如下
class Animal{
private int age; private int age1; private int age2; private int age3; private int age4;
}
此时我们能够计算得到一个Animal
对象占用的内存大小是(对象头12Byte+实例数据20Byte=32Byte)此时32由于是8的倍数所以无需进行填充补齐。那么此时如果还是二千万条数据的话,此对象占用内存应该是610MB,加上刚才集合中指针的数据76MB,那么加起来将近占用686MB,那么预期结果是否和我们的一样呢,我们重新启动程序观察,可以看到下图。可以看到和我们分析的数据是差不多的。
用Map存储对象
用Map存储对象计算内存大小有些麻烦了,众所周知Map的结构是如下图所示。
它是一个数组加链表(或者红黑树)的结构,而数组中存放的数据是Node对象。
static class Node<k,v> implements Map.Entry<k,v> { final int hash; final K key; V value; Node next;}
我们举例定义下面一个Map对象
Mapmap
此时我们可以自己计算一下一个Node对象需要的内存大小对象头12Byte+实例数据16Byte+对其填充4Byte=32Byte
,当然这里的key和value的值还需要另算,因为Node对象此时存放的仅仅是他们的引用而已。一个Animal对象所占用内存大小我们上面也说了是16Byte,所以这里一个Node对象占用的大小为32Byte+16Byte+16Byte=64Byte。
下面我们用实际例子来验证下我们的猜想
Mapmap = new HashMap>20000000);for (int i = 0; i > 20000000; i++) {map.put(new Animal(),new Animal());}
上面的例子在一个Map对象中存放二千万条数据,计算大概在内存中占用多少内存。
数组占用内存大小:我们先来计算一下数组占了多少,这里有个小知识点,在HashMap中初始化大小是按照2的倍数来的,比如你定义了大小为60,那么系统会给你初始化大小为64。所以我们定义为二千万,系统其实是会给我们初始化为33554432,所以此时仅仅HashMap中数组就占用了将近132MB
数据占用内存大小:我们上面计算了一个Node节点占用了64Byte,那么两千万条数据就占用了1280MB
两个占用内存大小相加我们可以知道大概系统中占用了1.4G内存的大小。那么事实是否是我们想象的呢?我们运行程序可以看到内存大小如图所示。可以看到结果确实和我们猜想的一样。
总结
回归到上面所说的需求,几百万数据放到内存中会把内存撑爆吗?这时候你可以通过自己的计算得到。最终我们那个需求经过我算出来其实占用内存量几百兆,对于4个G的堆内存来说其实远远还没达到撑爆的地步。所以有时候我们对任何东西都要存在怀疑的态度。大家可以到GitHub中下载代码自己在本地跑一下监测一下,并且可以自己定义几个对象然后计算看是不是和图中的内存大小一致。这样才能记忆更深刻。送给大家一句话从来如此,便对吗?。其实我写的文章里面也留了一个小坑,大家可以试着找找,是在对集合进行初始化计算那一块。
项目源码地址:https://github.com/modouxiansheng/Doraemon
参考
https://javaconceptoftheday.com/how-the-strings-are-stored-in-the-memory/
https://blog.csdn.net/belongtocode/article/details/103377187
精彩推荐
1、程序员喜欢的 5 款最佳代码比较工具
2、GitHub 上有个沙雕开发者,做了款斗图工具后火了...
3、中台是一个营销概念
4、如果我是面试官,我会问你 Spring 那些问题?
5、Mybatis的这些坑!把我坑惨了!
6、某小型公司持续集成工具 jenkins 实践
7、免费版的 IDEA 为啥不能使用 Tomcat ?
8、做一个完整的Java Web项目需要掌握的技能
vc++64位系统下long的长度为4个字节_新来的妹子把几百万数据放入了内存,系统立马爆了...相关推荐
- 新来的妹子把几百万数据放入了内存,系统立马爆了
点击上方蓝色字体,选择"标星公众号" 优质文章,第一时间送达 上一篇:这300G的Java资料是我师傅当年给我的,免费分享给大家 下一篇:这200G的Java实战资料是我师傅当年教 ...
- vc++64位系统下long的长度为4个字节_64位系统究竟牛逼在哪里?
想必大家都遇到过这样的问题:安装某个软件的时候,出现提示选择32位版本还是64位版本?我们也可以查看自己的电脑是32位还是64位系统. Windows Linux 大家可能知道32位和64位和系统有关 ...
- 32位与64位机器下各数据类型长度对比
64 位的优点:64 位的应用程序可以直接访问 4EB 的内存和文件大小最大达到4 EB(2 的 63 次幂):可以访问大型数据库.本文介绍的是64位下C语言开发程序注意事项. 1 32 位和 64 ...
- vc++64位系统下long的长度为4个字节_Java与系统硬件的亲密接触「伪共享」
作者:码洞来源:https://zhuanlan.zhihu.com/p/32764602 在解释[伪共享]这个概念之前,我们先来运行一段代码,小编的电脑上有4个core. 这个程序的逻辑是4个线程共 ...
- Win7系统64位环境下使用Apache——下载mod_jk
转载请注明出处:http://blog.csdn.net/dongdong9223/article/details/70313329 本文出自[我是干勾鱼的博客] 之前在几篇文章: Win7系统64位 ...
- VC6在64位Win7下调试无法退出的问题(缺少TLLOC.DLL和DM.dll)
win7 64位VC++6.0调试代码无法关闭窗口解决方法 VC6在64位Windows7下调试的时候,再结束调试,程序无法退出. 问题描述:当我击F5开始一个项目的调试时,程序在我设置的断点处停止, ...
- PyTorch在64位Windows下的Conda包
昨天发了一篇PyTorch在64位Windows下的编译过程的文章,有朋友觉得能不能发个包,这样就不用折腾了.于是,这个包就诞生了.感谢@Jeremy Zhou为conda包的安装做了测试. 更新:已 ...
- arduinowin7_Arduino在64位WIN7下无法安装驱动的解决办法
本人购买Arduino UNO R3连接在64位WIN7下,安装官方驱动后显示如下图错误: 硬件列表显示未知设备,如下图: 多次反复安装仍无法识别,在网上搜索都说是精简版的Win7 ghost删除了大 ...
- 在64位windows下使用instsrv.exe和srvany.exe创建windows服务
在64位windows下使用instsrv.exe和srvany.exe创建windows服务 在32位的windows下,包括windows7,windows xp以及windows 2003,都可 ...
最新文章
- 为啥不能用uuid做MySQL的主键?
- python编程入门与实践_Python编程入门到实践(二)
- 骗子是这样把1G硬盘变成120G的
- 我从Stack Overflow对64,000名开发人员的大规模调查中学到的东西
- 【转】C/S,B/S区别
- 判断字符串是否为空--string.Empty、string=、s.length==0
- Amr and Pins
- 原生js添加鼠标事件的兼容性写法
- uni-app开发环境搭建创建uni-app项目
- 在html显示php代码,html跳转php只显示源代码
- 传智播客reactnative_传智播客黑马前端36期(2018年)
- dsm php virtualbox,私有云测试环境搭建 黑群晖 For VirtualBox(XPEnoboot 5022.3 DSM_DS3615xs_5022)...
- win10安装k8s
- 不到最后一刻,绝不放弃!
- 学习方法论与相关建议
- php 过滤英文标点符号 过滤中文标点符号
- HMC5883L 电子指南针
- CAD怎么调整绘图区左下方显示坐标的框
- 基于阿里云IOT Studio和STM32的电机远程监测设计
- 使用xlsxwriter简单的将截图插入excel表格中
热门文章
- Html中元素的分类
- 【STL源码剖析读书笔记】【第5章】关联式容器之set、map、multiset和multimap
- HDOJ 1071 The area (纯数学题)
- [转载]细说ASP.NET Windows身份认证
- stm32中断向量控制器
- stm32 GPIO模式
- C++ Primer 5th笔记(chap 19 特殊工具与技术)两种不可移植的特性之“位域”
- 智能合约重构社会契约(7)以太坊总结
- 《研磨设计模式》chap7 抽象工厂 Abstract Factory
- 编译Bitcoin BCH configure: error: libdb_cxx headers missing ,终于解决了