返璞归真系列(基础回顾) 系列

基础不牢,地动山摇

在这里插入图片描述
在这里插入图片描述

标识符

是指在程序中我们自己定义内容,比如类的名字,方法的名字和变量的名字等等,都是标识符 例如类名Helloword

命名规则:硬性要求

  • 标识符可以包含英文字母26个(区分大小写)、0-9数字、$、和_(下划线)
  • 标识符不能以数字开头]
  • 标识符不能是关键字

常量

我们在java的世界里首先要明确常量与变量的区别,所谓常量就是指在java程序中固定不变的数据,而变量即为可以变化的量,一个变量每次只能保存一个数据,必须要明确保存的数据类型.
在这里插入图片描述

变量和数据类型

数据类型分类:

  • 基本数据类型:整数、浮点数、字符、布尔。
  • 引用数据类型:包括 类、接口、数组
    在这里插入图片描述
    变量定义
    数据类型 变量名= 数据值
    int a = 3;
    数据类型转换
    所谓数据类型转换可分为隐式转换(自动转换) 和强制转换。
    隐式转换:将取值范围小的类型自动提升为取值范围大的类型。
    在这里插入图片描述
    规则
    byte、short、char–>int–>long–>float–>double

强制转换
会损失精度

数据类型 变量名=(数据类型) 被转数据值;
在这里插入图片描述

  • 浮点转成整数,直接取消小数点,可能会损失精度
  • int强制转成short砍掉2个字节,可能造成数据丢失

ASCII编码表

在计算机的内部都是二进制的0、1数据,如何让计算机可以直接识别人类文字的问题呢?就产生出了编码表的概念
在这里插入图片描述

运算符

逻辑运算符
在这里插入图片描述
三元运算符

数据类型 变量名 =布尔类型表达式? 结果1 : 结果2

方法

所谓方法就是为了避免重复代码,解决代码冗余,将这个功能抽取出来

所谓方法有主要有类方法与成员方法。类方法不需要创建对象,在类初始化的时候方法产生,调用方式
类名.方法名,一般来说方法是静态的(static),成员方法,创建对象。

Java虚拟机内存区域划分

内存是计算机中的重要原件,临时存储区域,作用是运行程序,我们编写的程序是存放在硬盘中的,在硬盘中的程序是不会运行的,必须放进内存中才能运行,运行完毕后会清空内存
Java虚拟机要运行程序,必须要对内存进行空间的分配和管理
在这里插入图片描述

在这里插入图片描述
程序计数器

指向当前线程所执行的字节码指令的地址,通常也叫做行号指示器

如上方Helloworld.java文件所示 .java文件被javac指令编译成.class的字节码文件。字节码解释器会将编译好的字节码文件解释执行,这也真是java语言可以实现跨平台的真正原因。字节码解释器工作时,就是通过改变 程序计数器的值来选择下一条需要执行的字节码指令,分支,跳转,循环,异常处理等基础功能都需要依赖这个计数器来完成
所谓的多线程实则是通过CPU的时间片轮转调度算法来实现的,在一个时间片内,处理器只会执行一个线程中的指令。为了保证线程切换过程时可以恢复到原来的执行为止,每条线程都会有一个独立的程序计数器,各线程之间互不影响,独立存储。
在这里插入图片描述

java中的内存管控是由jvm来完成的,我们申请完内存空间后,jvm的垃圾回收机制会在合适的时间去释放这块内存空间.

Java虚拟机栈
我们平常所说的栈就是指虚拟机栈,或者说是虚拟机栈中的栈帧的局部变量表
虚拟机栈描述的是java方法执行的内存模型,方法的执行的同时会创建一个栈帧,用于存储方法中的局部变量表、操作数栈、动态链接、方法的出口等信息,每个方法从调用直到执行完成的过程,就对应着一个栈桢在虚拟机栈中入栈到出栈的过程。

javac:

    private void method() {

       int a = 2;

       float b = 4.5f;

       String name = "ss";

       Object object = new Object();

       float sum = a + b;

    }

javap -c +字节码文件路径

public void method();
    Code:
       0: iconst_2         //将整型2入栈(int a = 2;)
       1: istore_1         //将栈顶元素赋值给方法中第一个变量
       2: ldc           #2 //将4.5入栈(float b = 4.5f;)              // float 4.5f
       4: fstore_2         //将栈顶元素赋值给方法中第二个变量
       5: ldc           #3 //将ss入栈 (String name = "ss";)                // String ss
       7: astore_3         //将栈顶元素赋值给方法中第三个变量
       8: new           #4 //申请块堆内存,将申请到的堆内存地址压入栈(Object object = new Object();) // class java/lang/Object
      11: dup              //复制栈顶元素,并将赋值的栈顶元素压入栈
      12: invokespecial #1 //初始化对象(注意,此时当前栈顶的数据会出栈)// Method java/lang/Object."<init>":()V
      15: astore        4  //当前栈顶元素赋值给方法中,第四个变量
      17: iload_1          //将第一个变量值入栈(float sum = a + b;)
      18: i2f              //将栈顶int类型值转换为float类型值。
      19: fload_2          //将第二个变量值入栈
      20: fadd             //将栈顶两个数值相加结果压入栈
      21: fstore        5  //将栈顶的元素出栈赋值给方法中第五个变量
      23: return           //void函数返回
}

在这里插入图片描述

  • 方法被调用时,method()方法入栈,在虚拟栈中为method()方法初始化一个栈帧。
  • 将整形2入操作数栈
  • 将操作数栈栈顶元素赋值给方法中的第一个变量,即局部变量表中int a = 2;被初始化完成

具体虚拟机栈中每个区域:

  • 局部变量表

局部变量表中存放了方法的参数和方法内部定义的局部变量。boolean、byte、char、short、int、float、reference、returnAddress。

  1. 操作数栈
    Java虚拟机的解释执行引擎被称为”基于栈的执行引擎”,其中所指的栈就是指:操作数栈。操作数栈也常被称为操作栈。 和局部变量区一样,操作数栈也是被组织成一个以字长为单位的数组。但是和前者不同的是,它不是通过索引来访问,而是通过标准的栈操作:压栈和出栈来访问的。比如,如果某个指令把一个值压入到操作数栈中,稍后另一个指令就可以弹出这个值来使用。
    类似于数据结构里的栈

  2. 动态链接
    每个栈桢都包含一个指向运行时常量池(方法区的一部分)中该栈桢所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。字节码中的方法调用指令就以常量池中指向方法的符号引用为参数。这些符号引用一部分会在类加载阶段或第一次使用的时候转化为直接引用,被称为静态解析,另一部分会在每一次的运行期间转化为直接应用,这部分称为动态连接。就像我们所说的编译阶段与运行时阶段。

  3. 方法出口信息
    当一个方法被执行后,有两种方式退出该方法:

  4. 执行引擎遇到了任意一个方法返回的字节码指令
    调用者的PC计数器的值就可以作为返回地址,栈桢中很可能保存了这个计数器值

  5. 遇到了异常(没有在方法体内得到处理)
    返回地址是要通过异常处理器来确定.栈桢中一般不会保存这部分信息.

  • 嵌套调用时
    在这里插入图片描述

  • 递归调用时
    在这里插入图片描述
    无限递归时会产生StackOverFlow,栈溢出.

在这里插入图片描述

本地方法栈
本地方法栈和虚拟机栈很相似,区别在于本地方法栈是为Native方法服务,而虚拟机栈是为java方法服务,也会抛出StackOverFlowError异常,OutOfMemoryError异常
Java堆
java堆是Java虚拟机所管理的内存中最大的一块。java堆是被所有线程所共享的一块内存区域,虚拟机启动时创建,几乎所有对象的实例都存储在堆中,所有的对象和数组都要在堆上分配内存。
java堆是垃圾收集器(GC)管理的主要区域,java堆中可以划分出多线程私有的缓冲区,但是无论怎么划分对象的实例仍然存储在堆中。java堆允许处于不连续的物理内存空间中,只要逻辑连续即可。堆中如果没有空间完成实例分配无法扩展时将会抛出OutOfMemoryError异常。

JVM的堆分为两个区域,新生代,老年代。同时新生代又分为eden区、s0(From Survivor)、s1(To Survivor)三个区域
在这里插入图片描述
新生代主要存放的是那些很快就被GC回收掉的对象,或者不是特别大的对象,新生代中的GC操作时MonorGC,Eden区的对象经过一次MinorGC后如果仍然存活,会被迁移到Survivor区,在该区中每熬过一次MinorGC,年龄就会增加一岁,增加到一定程度就会被移动到老年代中

1、为什么堆区域要分代?

分代的目的就是为了性能的优化,提高GC的效率,反过来想一下,如果没有分代那么当堆发生GC操作时,会怎么做呢?GC会对整个堆进行扫描,找到该回收的对象回收。而我们的很多对象都是朝生夕死的,如果分代的话,我们把新创建的对象放到某一地方,当GC的时候先把这块存“朝生夕死”对象的区域进行回收,这样GC的效率会很高。

2、新生代为什么分区?

如果不分区,新生代每进行一次Minor GC,存活的对象就会被送到老年代。老年代很快被填满,触发Major GC(因为Major GC一般伴随着Minor GC,也可以看做触发了Full GC)。老年代的内存空间远大于新生代,进行一次Full GC消耗的时间比Minor GC长得多。你也许会问,执行时间长有什么坏处?频发的Full GC消耗的时间是非常可观的,这一点会影响大型程序的执行和响应速度,更不要说某些连接会因为超时发生连接错误了。

Survivor的存在意义,就是减少被送到老年代的对象,进而减少Full GC的发生,Survivor的预筛选保证,只有经历16次Minor GC还能在新生代中存活的对象,才会被送到老年代。

Survivor的存在意义,就是减少被送到老年代的对象,进而减少FullGC的发生,Survivor的预筛选保证,只有经历16Minor GC还能在新生代中的对象,才会被送到老年代。

  • 方法区
    方法区与堆一样所有线程所共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、及时编译器编译后的代码等数据。java虚拟机对方法区的限制非常宽松,除了和堆一样不需要连续的内存和可扩展,还可以不实现垃圾收集,相对而言,垃圾收集机制在这个区域出现的较少,当方法区无法分配足够内存时,将会抛出OutOfMemoryError异常。

  • 运行时常量池

运行时常量池是方法区的一部分,Class文件中除了有类的版本、字段、方法、接口、等描述信息外,还有一项信息就是常量池,用于存放编译时期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。一般来说,除了保存Class文件中的描述符号引用外,还会把翻译出来的直接引用也存储在运行时常量池中。

运行时常量池相对于Class文件常量池的另一个重要特征是具备动态性,java语言并不要求常量一定只有编译时期才能产生,也就是说并非预置入Class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可以将新的常量放入常量池,用的较多的如String的intern()方法。

运行时常量池是方法区的一部分,自然当方法区无法分配足够内存时,将会抛出OutOfMemoryError异常。

直接内存

 直接内存并不是虚拟机运行时数据区的一部分,也不是java虚拟机规范中定义的内存区域,但这部分内存也被频繁的使用,而且也会导致OutOfMemoryError异常。
 在JDK1.4中新加入了NIO类,引入了一种基于通道的与缓冲区的I/O方式,他可以是使用Native函数直接分配对外内存,然后通过位于堆中的DirectByteBuffer对象作为这块内存的引用进行操作,这样可以显著提高一些性能,因为避免Java堆和Native堆中来回的复制数据。

未完待续…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值