JAVA语言以及关于计算机的一些概念

负载均衡

负载均衡,英文名称为Load Balance,其意思就是分摊到多个操作单元上进行执行,例如Web服务器、FTP服务器、企业关键应用服务器和其它关键任务服务器等,从而共同完成工作任务。

栈、队列中“先进先出”,“后进先出”的含义

举例子说明.

栈的概念是弹压,就像子弹壳装弹,一粒一粒压进去,但是打出来的时候是从上面打出来的,最先压进去的最后弹出来,如果进去顺序是123,打出来顺序是321,这就是后进先出

队列的概念就是我们平时排队,按次序来,你排在第1个,那你就第一个轮到,就是先进先出,先到先来。

JAVA中的NATIVE方法

本地方法,可以编译成C语言,native方法在JVM中运行时数据区也和其它方法不一样,它有专门的本地方法栈。native方法主要用于加载文件和动态链接库,由于Java语言无法访问操作系统底层信息(比如:底层硬件设备等),这时候就需要借助C语言来完成了。被native修饰的方法可以被C语言重写。

如何设置JAVA虚拟机JVM启动内存参数

1 TOMCAT

2 ECLIPSE

3 JAVA直接编译

4 WEBLOGIC

各服务器容器都可以找到相关方法进行设置。具体设置,请参考相关文档。

JAVA 虚拟机

Java虚拟机是用于支撑JAVA在不同硬件环境下运行java代码。

JVM是Java Virtual Machine(Java虚拟机)的缩写,是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机主要由字节码指令集、寄存器、栈、垃圾回收堆和存储方法域等构成。 JVM屏蔽了与具体操作系统平台相关的信息,使Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。JVM在执行字节码时,实际上最终还是把字节码解释成具体平台上的机器指令执行。

JVM声明周期

JVM伴随Java程序的开始而开始,程序的结束而停止。一个Java程序会开启一个JVM进程,一台计算机上可以运行多个程序,也就可以运行多个JVM进程。

JVM将线程分为两种:守护线程和普通线程。守护线程是JVM自己使用的线程,比如垃圾回收(GC)就是一个守护线程。普通线程一般是Java程序的线程,只要JVM中有普通线程在执行,那么JVM就不会停止。

JVM内存模型组成

JVM内存模型主要由堆内存、方法区、程序计数器、虚拟机栈和本地方法栈组成,其组成的结构如下图所示。

其中,堆和方法区是所有线程共有的,而虚拟机栈,本地方法栈和程序计数器则是线程私有的。

堆内存

堆内存是所有线程共有的,可以分为两个部分:年轻代和老年代。下图中的Perm代表的是永久代,但是注意永久代并不属于堆内存中的一部分,同时jdk1.8之后永久代也将被移除。

堆内存是我们在生产环境中进行内存性能调优中的一个重要的内容,而内存回收的一些机制和算法也是常见的考点,大家可以访问下面的链接:Java性能优化之JVM GC

方法区

方法区与Java堆一样,是各个线程共享的区域,它用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译(JIT)后的代码等数据。

由于程序中所有的线程共享一个方法区,所以访问方法区的信息必须确保线程是安全的。如果有两个线程同时去加载一个类,那么只能有一个线程被允许去加载这个类,另一个必须等待。

在程序运行时,方法区的大小是可以改变的,程序在运行时可以扩展。同时,方法区里面的对象也可以被垃圾回收,但条件非常严苛,必须在该类没有任何引用的情况下才能被GC回收。

程序计数器

在JVM的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

JVM的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,为了各条线程之间的切换后计数器能恢复到正确的执行位置,所以每条线程都会有一个独立的程序计数器。

当线程正在执行一个Java方法,程序计数器记录的是正在执行的JVM字节码指令的地址;如果正在执行的是一个Natvie(本地方法),那么这个计数器的值则为空(Underfined)。

程序计数器占用的内存空间很少,也是唯一一个在JVM规范中没有规定任何OutOfMemoryError(内存不足错误)的区域。

Java虚拟机栈

与程序计数器一样,Java虚拟机栈也是线程私有的,用通俗的话将它就是我们常常听说到堆栈中的那个“栈内存”。虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表(局部变量表需要的内存在编译期间就确定了所以在方法运行期间不会改变大小),操作数栈,动态链接,方法出口等信息。每一个方法从调用至出栈的过程,就对应着栈帧在虚拟机中从入栈到出栈的过程。

本地方法栈

栈作为一种线性的管道结构,遵循先进后出的原则。主要用于存储本地方法的局部变量表,本地方法的操作数栈等信息。当栈内的数据在超出其作用域后,会被自动释放掉。

本地方法栈是在程序调用或JVM调用本地方法接口(Native)时候启用。

Spring中BEAN的作用域singleton和prototype的不同

Spring中的BEAN默认作用域为singleton,当我们需要获得bean时,容器将按照BEAN的生命周期过程,实例化相应的BEAN,并且放入Spring IOC容的缓存池中,并将BEAN的引用返回给调用者,由Spring对这些BEAN进行后续的生命管理。下次如果再使用到这个BEAN,可以直接从缓存中获得。

如果Bean的作用范围是scope=‘prototype’,当我们需要Bean时,容器在实例化Bean以后,会将Bean返回给调用者,调用者负责Bean后续生命的管理,Spring不再负责管理这个Bean的生命周。且第二次用getBean()时,生命周期的方法会继续调用,因为prototype范围的Bean每次都返回新的实例。

ApplicationContext和BeanFactory的区别?

初始化的区别:ApplicationContext在初始化应用上下文时,就实例化所有单实例的Bean.而BeanFactory在初始化容器时,并未实例化Bean,直到第一次访问某个Bean时才实例化目标Bean.因此ApplicationContext的初始化时间会比BeanFactory的时间稍微长一点。

其次,还有一个最大不同,ApplicationContext会利用反射机制,自动识别配置文件中定义的BeanPostProcessor,InstantiationAwareBeanPostProcessor和BeanFactoryPostProcessor,并自动将他们注册到应用上下文。但是BeanFactory需要在代码中手工调用addBeanPostProcessor()进行注册。

Java中如何理解overload , override

overload实现多态性,同一个类里面,同样的方法,可以包含不同的参数。

override,子类继承父类时,可以定义同名同参数的方法,这样当子类调用这一方法时,自动调用子类的方法,父类的方法可以通过super来调用。

ClassLoader类加载的工作机制

1.装载:查找和导入Class文件

2.链接:执行校验,准备和解析的步骤。校验,检查Class文件数据的正确性。准备,将类的静态变量分配存储空间。解析,将符号引用转成直接引用。

3.初始化:将类的静态变量,,静态代码块执行初始化工作。

JVM运行时,产生三个类加载器:根加载器,ExtClassLoader(扩展类加载器),AppClassLoader(系统类加载器)。根加载由C++编写,负责加载RE核心类库。如JRE目录下的rt.jar包。扩展类加载JRE的扩展目录ext中的JAR包。系统加载负责装载Classpath路径下的JAR包。

三个加载器按照全盘负责委托机制加载,先通过父类进行类加载,如果加载不到,在通过子类加载。

符号引用和直接引用

 符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能够无歧义的定位到目标即可。例如,在Class文件中它以CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info等类型的常量出现。

  直接引用可以是

(1)直接指向目标的指针(比如,指向“类型”【Class对象】、类变量、类方法的直接引用可能是指向方法区的指针)

(2)相对偏移量(比如,指向实例变量、实例方法的直接引用都是偏移量)

(3)一个能间接定位到目标的句柄

直接引用是和虚拟机的布局相关的,同一个符号引用在不同的虚拟机实例上翻译出来的直接引用一般不会相同。如果有了直接引用,那引用的目标必定已经被加载入内存中了。

Java中的String,StringBuilder,StringBuffer三者的区别

首先说运行速度,或者说是执行速度,在这方面运行速度快慢为:StringBuilder > StringBuffer > String

String最慢的原因:

     String采用连接运算符(+)效率低下,都是上述循环、大批量数据情况造成的,每做一次"+"就产生个StringBuilder对象,然后append后就扔掉。下次循环再到达时重新产生个StringBuilder对象,然后append字符串,如此循环直至结束。如果我们直接采用StringBuilder对象进行append的话,我们可以节省创建和销毁对象的时间。如果只是简单的字面量拼接或者很少的字符串拼接,性能都是差不多的。

  String为字符串常量,而StringBuilder和StringBuffer均为字符串变量,即String对象一旦创建之后该对象是不可更改的,但后两者的对象是变量,是可以更改的。

在线程安全上,StringBuilder是线程不安全的,而StringBuffer是线程安全的

  如果一个StringBuffer对象在字符串缓冲区被多个线程使用时,StringBuffer中很多方法可以带有synchronized关键字,所以可以保证线程是安全的,但StringBuilder的方法则没有该关键字,所以不能保证线程安全,有可能会出现一些错误的操作。所以如果要进行的操作是多线程的,那么就要使用StringBuffer,但是在单线程的情况下,还是建议使用速度比较快的StringBuilder。

String:适用于少量的字符串操作的情况

StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况

StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况

线程安全

线程安全是多线程编程时的计算机程序代码中的一个概念。在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。简单说,就是多个线程对一个数据或者对象进行内容的修改时,不会出现数据冲突和不一致的问题。

HashMap,Hashtable,ConcurrentHashMap

HashMap中键值 允许为空 并且是非同步

Hashtable中键值不允许为空 是同步

继承不同,但都实现了Map接口

                            HashMap                Hashtable

父类                   AbstractMap          Dictiionary

是否同步              否                            是

k,v可否null        是                            否

现在都会用ConcurrentHashMap进行同步操作。ConcurrentHashMap仅仅锁定map的某个部分,而Hashtable则会锁定整个map。


volatile 关键字

Volatile原理

Java语言提供了一种稍弱的同步机制,即volatile变量,用来确保将变量的更新操作通知到其他线程。当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。

在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞,因此volatile变量是一种比sychronized关键字更轻量级的同步机制。

  当对非 volatile 变量进行读写的时候,每个线程先从内存拷贝变量到CPU缓存中。如果计算机有多个CPU,每个线程可能在不同的CPU上被处理,这意味着每个线程可以拷贝到不同的 CPU cache 中。

而声明变量是 volatile 的,JVM 保证了每次读变量都从内存中读,跳过 CPU cache 这一步。

当一个变量定义为 volatile 之后,将具备两种特性:

1.保证此变量对所有的线程的可见性,这里的“可见性”,如本文开头所述,当一个线程修改了这个变量的值,volatile 保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。但普通变量做不到这点,普通变量的值在线程间传递均需要通过主内存(详见:Java内存模型)来完成。

2.禁止指令重排序优化。有volatile修饰的变量,赋值后多执行了一个“load addl $0x0, (%esp)”操作,这个操作相当于一个内存屏障(指令重排序时不能把后面的指令重排序到内存屏障之前的位置),只有一个CPU访问内存时,并不需要内存屏障;(什么是指令重排序:是指CPU采用了允许将多条指令不按程序规定的顺序分开发送给各相应电路单元处理)。

volatile 性能:

volatile 的读性能消耗与普通变量几乎相同,但是写操作稍慢,因为它需要在本地代码中插入许多内存屏障指令来保证处理器不发生乱序执行。

final 关键字

针对Java语言中的final关键字,想必都不陌生了。本来主要是来对final做关键字做一个总结。

final关键字用法

1 修饰类当用final去修饰一个类的时候,表示这个类不能被继承。注意:a. 被final修饰的类,final类中的成员变量可以根据自己的实际需要设计为fianl。b. final类中的成员方法都会被隐式的指定为final方法。说明:在自己设计一个类的时候,要想好这个类将来是否会被继承,如果可以被继承,则该类不能使用fianl修饰,在这里呢,一般来说工具类我们往往都会设计成为一个fianl类。在JDK中,被设计为final类的有String、System等。

2  修饰方法

被final修饰的方法不能被重写。

注意:

a. 一个类的private方法会隐式的被指定为final方法。

b. 如果父类中有final修饰的方法,那么子类不能去重写。

3  修饰成员变量

注意:

a. 必须要赋初始值,而且是只能初始化一次。

4 修饰成员变量

注意:

a. 必须初始化值。

b. 被fianl修饰的成员变量赋值,有两种方式:1、直接赋值 2、全部在构造方法中赋初值。

c. 如果修饰的成员变量是基本类型,则表示这个变量的值不能改变。

d. 如果修饰的成员变量是一个引用类型,则是说这个引用的地址的值不能修改,但是这个引用所指向的对象里面的内容还是可以改变的。

乐观锁和悲观锁

悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。再比如Java里面的同步原语synchronized关键字的实现也是悲观锁。

乐观锁:顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。

Compare And Swap(CAS)

CAS是一种有名的无锁(lock-free)算法。也是一种现代 CPU 广泛支持的CPU指令级的操作,只有一步原子操作,所以非常快。而且CAS避免了请求操作系统来裁定锁的问题,不用麻烦操作系统,直接在CPU内部就搞定了。

CAS有三个操作参数:

内存位置V(它的值是我们想要去更新的)

预期原值A(上一次从内存中读取的值)

新值B(应该写入的新值)

CAS的操作过程:将内存位置V的值与A比较(compare),如果相等,则说明没有其它线程来修改过这个值,所以把内存V的的值更新成B(swap),如果不相等,说明V上的值被修改过了,不更新,而是返回当前V的值,再重新执行一次任务再继续这个过程。

所以,当多个线程尝试使用CAS同时更新同一个变量时,其中一个线程会成功更新变量的值,剩下的会失败。失败的线程可以重试或者什么也不做。

简单来说,CAS 的含义是“我认为原有的值应该是什么,如果是,则将原有的值更新为新值,否则不做修改,并告诉我这个值现在是多少”。(这段描述引自《Java并发编程实践》)

map迭代的四种方式

方式一:通过Map.keySet遍历key和value

方式二:通过Map.entrySet遍历key和value,推荐,尤其是容量大时.

方式三:通过Map.entrySet使用iterator遍历key和value.

方式四:通过Map.values()遍历所有的value,但不能遍历key.

JVM调优内容太多,可以参考下面帖子

JVM调优总结

HASH算法

hash算法也称散列算法,将不同长度的值输出成固定长度的值,然后通过键值对的方式,存储在散列表中,这样查找相应的值,就可以通过相应的散列KEY来查找,而KEY存的就是对象的地址,就可以实现快速查找。理论上时间复杂度可以O(1)。


Java 对象序 列化的机制

Socket通信

SPRING IOC 和AOP

你可能感兴趣的:(JAVA语言以及关于计算机的一些概念)