java堆、栈、方法区、bit、byte及图片在内存中的大小

一、java中类型所占字节数以及可表示的数字个数



基本概念
字节的单位是byte。位的单位是bit。1byte=8bit。
1bit能表示多少个数字——最小0,最大1。2的1次方个。
2bit能表示多少个数字——最小00,最大11。2的2次方个。
3bit能表示多少个数——最小000,最大111。2的3次方个。
依此类推—8bit可以表示2的8次方共256个数字。
若数没有正负之分可表示0—255共256个数字。若有正负之分可表示-128到+127共256个,因为8位里有一个位表示正负。
 


得出结论
java中基本类型内存中所占字节整理
 
整类型
类型 内存中所占字节byte所占位bit 可表示数字个数
byte   1  8 2的8次方
short 2 16 2的16次方
int         4 32 2的32次方
long 8 64 2的64次方
 
字符类型
类型 内存中所占字节byte所占位bit 可表示数字个数
char 2 16 2的16次方
 
浮点类型
类型 内存中所占字节byte所占位bit 可表示数字个数
float         432 2的32次方
double 8 64 2的64次方
 
布尔型
类型 内存中所占字节byte所占位bit 可表示数字个数
boolean 1 2的1次方




二、


计算一张100px*100px的图片在内存中会占用多大内存

  在实际开发当中我们经常会忽视如题问题,只是知道图片越小越好,甚至根本不知道如何计算,今天笔者就抛砖引玉一把!

  Android中一张图片(Bitmap)占用的内存主要和以下几个因数有关:图片长度,图片宽度,单位像素占用的字节数。
  计算方法:一张图片(Bitmap)占用的内存=图片长度*图片宽度*单位像素占用的字节数
长度和宽度不用多做解释 ,单位是像素;关键是单位像素占用的字节数如何获得呢?
这里不得不提一下Bitmap创建的API
   Bitmap bitmap = BitmapFactory.decodeResource(Resources res,int id, BitmapFactory.Options opts);
  单位像素占用的字节数由其参数BitmapFactory.OptionsinPreferredConfig变量决定,默认为Bitmap.Config.ARGB_8888
  inPreferredConfig为Bitmap.Config类型,
   Bitmap.Config 是个枚举类型,它可以为以下值
ALPHA_8 Each pixel is stored as a single translucency (alpha) channel. 
This is very useful to efficiently store masks for instance. No color information is stored. With this configuration, each pixel requires 1 byte of memory.
此时图片只有alpha值,没有RGB值,一个像素占用1个字节
ARGB_4444 This field is deprecated. Because of the poor quality of this configuration, it is advised to use ARGB_8888instead.  
这种格式的图片,看起来质量太差,已经不推荐使用。
Each pixel is stored on 2 bytes. The three RGB color channels and the alpha channel (translucency) are stored with a 4 bits precision (16 possible values.) This configuration is mostly useful if the application needs to store translucency information but also needs to save memory. It is recommended to use ARGB_8888 instead of this configuration.
一个像素占用2个字节,alpha(A)值,Red(R)值,Green(G)值,Blue(B)值各占4个bites,共16bites,即2个字节
ARGB_8888  Each pixel is stored on 4 bytes. Each channel (RGB and alpha for translucency) is stored with 8 bits of precision (256 possible values.) This configuration is very flexible and offers the best quality. It should be used whenever possible
一个像素占用4个字节,alpha(A)值,Red(R)值,Green(G)值,Blue(B)值各占8个bites,共32bites,即4个字节
这是一种高质量的图片格式,电脑上普通采用的格式。它也是Android手机上一个BitMap的默认格式。
RGB_565  Each pixel is stored on 2 bytes and only the RGB channels are encoded: red is stored with 5 bits of precision (32 possible values), green is stored with 6 bits of precision (64 possible values) and blue is stored with 5 bits of precision. This configuration can produce slight visual artifacts depending on the configuration of the source. For instance, without dithering, the result might show a greenish tint. To get better results dithering should be applied. This configuration may be useful when using opaque bitmaps that do not require high color fidelity.
一个像素占用2个字节,没有alpha(A)值,即不支持透明和半透明,Red(R)值占5个bites ,Green(G)值占6个bites  ,Blue(B)值占5个bites,共16bites,即2个字节.对于没有透明和半透明颜色的图片来说,该格式的图片能够达到比较的呈现效果,相对于ARGB_8888来说也能减少一半的内存开销。因此它是一个不错的选择。另外我们通过android.content.res.Resources来取得一个张图片时,它也是以该格式来构建BitMap的.
Android4.0开始,该选项无效。即使设置为该值,系统任然会采用 ARGB_8888来构造图片
所以一张100px*100px的图片在内存中占用的内存数为:
图片格式 计算公式 占用内存大小
ALPHA_8 100*100 10000b
ARGB_4444 100*100*2 20000b
ARGB_8888 100*100*4 40000b
RGB_565 100*100*2 20000b
 

三、Java内存图以及堆、栈、常量区、静态区、方法区的区别


这里写图片描述 
如果是一个类里面的静态成员变量和静态成员方法,它是存储在方法区的,静态成员变量是在方法区的静态域里面,而静态成员方法是在方法区的class二进制信息里面(.class文件和方法区里面的二进制信息不一样,读取.class文件按照虚拟机需要的格式存储在方法区。这种格式包括数据结构方面),静态成员和静态成员方法使用时不用创建对象,即类加载初始化后就可以使用,并且是线程共享的。

通过图中分析,很多问题也能够迎刃而解,比如不同线程调用方法为什么是线程安全的。局部变量存储在哪儿里(栈中),成员变量存储在哪儿里(静态成员变量存储在方法区,非静态成员变量存储在堆区),为什么局部变量不能够static修饰等(局部变量存储在栈区,在方法调用时不能够自动初始化必须由程序员手动初始化,否则会报错,归根结底是由于static变量和局部变量存储的位置不一样。)。

内存图原稿下载

Java内存空间个人理解

堆:堆主要存放Java在运行过程中new出来的对象,凡是通过new生成的对象都存放在堆中,对于堆中的对象生命周期的管理由Java虚拟机的垃圾回收机制GC进行回收和统一管理。类的非静态成员变量也放在堆区,其中基本数据类型是直接保存值,而复杂类型是保存指向对象的引用,非静态成员变量在类的实例化时开辟空间并且初始化。所以你要知道类的几个时机,加载-连接-初始化-实例化。详情请查看博客:Java中 类的加载概述和加载时机

栈:栈主要存放在运行期间用到的一些局部变量(基本数据类型的变量)或者是指向其他对象的一些引用,因为方法执行时,被分配的内存就在栈中,所以当然存储的局部变量就在栈中咯。当一段代码或者一个方法调用完毕后,栈中为这段代码所提供的基本数据类型或者对象的引用立即被释放;

常量池:常量池是方法区的一部分内存。常量池在编译期间就将一部分数据存放于该区域,包含基本数据类型如int、long等以final声明的常量值,和String字符串、特别注意的是对于方法运行期位于栈中的局部变量String常量的值可以通过 String.intern()方法将该值置入到常量池中。

静态域:位于方法区的一块内存。存放类中以static声明的静态成员变量

方法区:是各个线程共享的内存区域,它用于存储class二进制文件,包含了虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。它有个名字叫做Non-Heap(非堆),目的是与Java堆区分开。

需要特别注意的是:

方法区是线程安全的。由于所有的线程都共享方法区,所以,方法区里的数据访问必须被设计成线程安全的。例如,假如同时有两个线程都企图访问方法区中的同一个类,而这个类还没有被装入JVM,那么只允许一个线程去装载它,而其它线程必须等待 !

最后总结起来就是:

栈:为即时调用的方法开辟空间,存储局部变量值(基本数据类型),局部变量引用。注意:局部变量必须手动初始化。 
堆:存放引用类型的对象,即new出来的对象、数组值、类的非静态成员变量值(基本数据类型)、非静态成员变量引用。其中非静态成员变量在实例化时开辟空间初始化值。更具体点,个人感觉非静态成员变量是放在堆的对象中。 
方法区:存放class二进制文件。包含类信息、静态变量,常量池(String字符串和final修饰的常量值等),类的版本号等基本信息。因为是共享的区域,所以如果静态成员变量的值或者常量值(String类型的值能够非修改,具体请查看博客)被修改了直接就会反应到其它类的对象中。

成员变量与局部变量总结:

一:在方法中声明的变,即该变量是局部变量,每当程序调用方法时,系统都会为该方法建立一个方法栈,其所在方法中声明的变量就放在方法栈中,当方法结束系统会释放方法栈,其对应在该方法中声明的变量随着栈的销毁而结束,这就局部变量只能在方法中有效的原因<1>在方法中生明的变量可以是基本类型的变量,也可以是引用类型的变量,(1)当声明是基本类型的变量的时,其变量名及值(变量名及值是两个概念)是放在方法栈中(2)当声明的是引用变量时,所声明的变量(该变量实际上是在方法中存储的是内存地址值)是放在方法的栈中,该变量所指向的对象是放在堆类存中的》》》二:在类中声明的变量是成员变量,也叫全局变量,放在堆中的,<1>同样在类中声明的变量即可是基本类型的变量 也可是引用类型的变量(1)当声明的是基本类型的变量其变量名及其只时放在堆类存中的,(2)引用类型时,其声明的变量仍然会存储一个内存地址值,该内存地址值指向所引用的对象

下面给大家看一个Java代码例子:

声明一个类:

public class A {
    public final String tempString="world";//这里可以把final去掉,结果等同!!
    public final char[] charArray="Hello".toCharArray();
    public char[] getCharArray() {
        return charArray;
    }
    public String getTempString() {
        return tempString;
    }
}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

创建测试类:

public class TestA {
    public static void main(String[] args) {
        A a1=new  A();
        A a2=new A();

        System.out.println(a1.charArray==a2.charArray);
        System.out.println(a1.tempString==a2.tempString);
    }
}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

输出结果:

false
true
   
   
  • 1
  • 2

要想明白上面字符串对比为什么输出为true你必须知道:

这里写图片描述

该图片截自《深入理解Java虚拟机》

  • 一个Class字节码文件的Class字节码文件对象只有一个常量池,常量池被所有线程共享。
  • 在常量池中,字符串被存储为一个字符序列,每个字符序列都对应一个String对象,该对象保存在堆中。所以也就是说为什么String temp="xxx";能够当成一个对象使用!!
  • 如果多个线程去访问A类中的String字符串,每次都会到常量区中去找该字符序列的引用。
  • 所以访问A类被创建的两个A类型对象的String字符串对比会输出true。

如果对字符串输出为true还是不懂,可以参考这篇博客:字符串被存储的原理过程

那么为什么final类型的字符数组就不为true了呢??

申明(不管是通过new还是通过直接写一个数组)一个数组其实在Java中就等同创建了一个对象,即每次创建类的对象都会自动创建一个新的数组空间。

其中要注意的是:常量池中存储字符数组只是存储的是每个字符或者字符串。

为了证明每次获取的final数组地址不一样,并且数组中的字符都会存储在常量池中,我们需要参考另外一个代码:

public class A {
    public String tempString="world";
    public final String tempStringArray[]={"Fire","Lang"};
    public final char[] charArray={'h','e','l','l','o'};
    public Character charx='l';

    public char[] getCharArray() {
        return charArray;
    }
    public String getTempString() {
        return tempString;
    }
    public String[] getTempStringArray() {
        return tempStringArray;
    }
    public Character getCharx() {
        return charx;
    }
}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

测试代码:

public class TestA {
    public static void main(String[] args) {
        A a1=new  A();
        A a2=new A();
        System.out.println(a1.tempString==a2.tempString);
        System.out.println(a1.tempStringArray==a2.tempStringArray);//看这里
        System.out.println("#####################");//看这里
        System.out.println(a1.tempStringArray[0]==a2.tempStringArray[0]);
        System.out.println(a1.tempStringArray[0]=="Fire");
        System.out.println("#####################");
        System.out.println(a1.charArray==a2.charArray);
        System.out.println(a1.charx==a2.charx);
    }
}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

输出:

true
false
#####################
true
true
#####################
false
true
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

可以看到每次输出的final数组地址都不一样,最重要的是String类型的数组地址也都不一样!!但是String类型数组中的每个字符串都存储在常量池中。

所以可以肯定的是字符串和其它能够确定值的final字面量值是存储在常量池的!!并且在方法区内存中只有一份!!与所有线程共享访问!!

常量池存储的项目类型:

这里写图片描述














注:

参考资料:http://blog.sina.com.cn/s/blog_67aaf44401013cwl.html

https://www.cnblogs.com/YuangPong/p/6694512.html
http://blog.csdn.net/Marvel__Dead/article/details/69220153?locationNum=5&fps=1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值