JAVA知识点总结

来源:互联网 发布:蔬菜网络采购平台 编辑:程序博客网 时间:2024/06/07 15:31

一、关于java基础 
1、一致性hash 
(1)提出一致性hash是为了解决什么问题? 

        用于解决服务器均衡问题:比如你有 N 个 cache 服务器(后面简称 cache ),那么如何将一个对象 object 映射到 N 个 cache 上呢,可以采用一致性hash算法计算 object 的 hash 值,然后均匀的映射到到 N 个 cache ;

(2)hash的性能评估指标

         单调性是指如果已经有一些内容通过哈希分派到了相应的缓冲中,又有新的缓冲加入到系统中。哈希的结果应能够保证原有已分配的内容可以被映射到新的缓冲中去,而不会被映射到旧的缓冲集合中的其他缓冲区。
         分散性(Spread):在分布式环境中,终端有可能看不到所有的缓冲,而是只能看到其中的一部分。当终端希望通过哈希过程将内容映射到缓冲上时,由于不同终端所见的缓冲范围有可能不同,从而导致哈希的结果不一致,最终的结果是相同的内容被不同的终端映射到不同的缓冲区中。这种情况显然是应该避免的,因为它导致相同内容被存储到不同缓冲中去,降低了系统存储的效率。分散性的定义就是上述情况发生的严重程度。好的哈希算法应能够尽量避免不一致的情况发生,也就是尽量降低分散性。
         负载(Load):负载问题实际上是从另一个角度看待分散性问题。既然不同的终端可能将相同的内容映射到不同的缓冲区中,那么对于一个特定的缓冲区而言,也可能被不同的用户映射为不同的内容。与分散性一样,这种情况也是应当避免的,因此好的哈希算法应能够尽量降低缓冲的负荷。
2、java锁机制 
(1)synchronized关键字、volatile关键字 

        关键字volatile的作用是强制从公共堆栈中取得变量的值,而不是从线程私有数据栈中取得变量的值。使用关键字volatile增加了实例变量在多个线程之间的可见性。但是volatile关键字最致命的缺点是不支持原子性。
   下面将关键字volatile和synchronized进行比较:
   1)关键字volatile是线程同步的轻量级实现,所以volatile性能肯定比synchronized要差,并且volatile只能修饰于变量,而synchronized可以修饰方法,以及代码块。随着JDK新版本的发布,synchronized关键字在执行效率上得到很大提升,在开发中使用synchronized关键字的比率还是比较大的。
   2)多线程访问volatile不会发生阻塞,而synchronized会出现阻塞。
   3)volatile能保证数据的可见性,但不能保证原子性;而synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存和公共内存中的数据做同步。
   4)关键字volatile解决的是变量在多个线程之间的可见性;而synchronized关键字解决的多个线程之间访问资源的同步性。
     线程安全包含原子性和可见性两个方面,java的同步机制都是围绕这两个方面来确保线程安全的。
     关键字volatile主要使用的场合是在多个线程中可以感知实例变量被更改了,并且可以获得最新的值使用,也就是用多线程读取变量时可以获得最新值使用。
     关键字volatile提示线程每次从共享内存中读取变量,而不是从私有内存中读取,这样就保证了同步数据的可见性。
     关键字synchronized可以保证在同一时刻,只有一个线程可以执行某一个方法或某一个代码块。它包含两个特征:互斥性和可见性。同步synchronized不仅可以解决一个线程看到对象处于不一致的状态,还可以保证进入同步方法或者同步代码块的每个线程,都看到由同一个锁保护之前所有的修改效果。
(2)java.util.concurrent.locks包 

      1).Lock能完成几乎所有synchronized的功能,并有一些后者不具备的功能,如锁投票、定时锁等候、可中断锁等候等
      2).synchronized 是Java 语言层面的,是内置的关键字;Lock 则是JDK 5中出现的一个包,在使用时,synchronized 同步的代码块可以由JVM自动释放;Lock 需要程序员在finally块中手工释放,如果不释放,可能会引起难以预料的后果(在多线程环境中)。
(3)synchronized和ReentrantLock(重入锁)的区别,synchronized用在代码块、方法、静态方法时锁的都是什么 

       1)sychronized是java中最基本同步互斥的手段,可以修饰代码块,方法,类.
         在修饰代码块的时候需要一个reference对象作为锁的对象.
         在修饰方法的时候默认是当前对象作为锁的对象.
         在修饰类时候默认是当前类的Class对象作为锁的对象.

         synchronized会在进入同步块的前后分别形成monitorenter和monitorexit字节码指令.在执行monitorenter指令时会尝试获取对象的锁,如果此没对象没有被锁,或者此对象已经被当前线程锁住,那么锁的计数器加一,每当monitorexit被锁的对象的计数器减一.直到为0就释放该对象的锁.由此synchronized是可重入的,不会出现自己把自己锁死.
       2)ReentrantLock

              以对象的方式来操作对象锁.相对于sychronized需要在finally中去释放锁。

          3)区别 

     1.ReentrantLock等待可中断,Synchronized不中断
           在持有锁的线程长时间不释放锁的时候,等待的线程可以选择放弃等待.   tryLock(long timeout, TimeUnit unit)。ReentrantLock 拥有Synchronized相同的并发性和内存语义,此外还多了 锁投票,定时锁等候和中断锁等候。线程A和B都要获取对象O的锁定,假设A获取了对象O锁,B将等待A释放对O的锁定。 如果使用 synchronized ,如果A不释放,B将一直等下去,不能被中断。如果使用ReentrantLock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待,而干别的事情。
      ReentrantLock获取锁定与三种方式: 
        a)  lock(), 如果获取了锁立即返回,如果别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁
        b) tryLock(), 如果获取了锁立即返回true,如果别的线程正持有锁,立即返回false;
        c) tryLock (long timeout, TimeUnit  unit),   如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false;
        d) lockInterruptibly: 如果获取了锁定立即返回,如果没有获取锁定,当前线程处于休眠状态,直到或者锁定,或者当前线程被别的线程中断
    2.公平锁ReentrantLock与非公平锁synchronized
         按照申请锁的顺序来一次获得锁称为公平锁.synchronized的是非公平锁,ReentrantLock可以通过构造函数实现公平锁.    new RenentrantLock(boolean fair)
    3.绑定多个Condition
        通过多次newCondition可以获得多个Condition对象,可以简单的实现比较复杂的线程同步的功能.通过await(),signal()
        此外,synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定,但是使用Lock则不行,lock是通过代码实现的,要保证锁定一定会被释放,就必须将 unLock()放到finally{} 中。

      在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetrantLock,但是在资源竞争很激烈的情况下,Synchronized的性能会下降几十倍,但是ReetrantLock的性能能维持常态。  

3、ConcurrentHashMap的原理,都用的是哪种锁,segment有没可能增大(不会,只会增大每个segment中的entry数组) 

       ConcurrentHashMap为了提高本身的并发能力,在内部采用了一个叫做Segment的结构,一个Segment其实就是一个类Hash Table的结构,Segment内部维护了一个链表数组。

       ConcurrentHashMap定位一个元素的过程需要进行两次Hash操作,第一次Hash定位到Segment,第二次Hash定位到元素所在的链表的头部,因此,这一种结构的带来的副作用是Hash的过程要比普通的HashMap要长,但是带来的好处是写操作的时候可以只对元素所在的Segment进行加锁即可,不会影响到其他的Segment,这样,在最理想的情况下,ConcurrentHashMap可以最高同时支持Segment数量大小的写操作(刚好这些写操作都非常平均地分布在所有的Segment上),所以,通过这一种结构,ConcurrentHashMap的并发能力可以大大的提高。

4、进程和线程的区别 

     进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

5、AtomicInteger实现原理(CAS自旋)

      CAS(Compare and Swap)原理:通过申明一个volatile (内存锁定,同一时刻只有一个线程可以修改内存值)类型的变量,再加上unsafe.compareAndSwapInt的方法,来保证实现线程同步的。

      从内存领域来说这是乐观锁,因为它在对共享变量更新之前会先比较当前值是否与更新前的值一致,如果是,则更新,如果不是,则无限循环执行(称为自旋),直到当前值与更新前的值一致为止,才执行更新。

6、值传递和引用传递 

     值类型默认存放在栈中,但当值类型是在引用类型中声明的时候,则存放在其所在的引用类型的堆中。引用类型存放在堆中。其在堆中的内存地址存放在栈中。

     值类型参数可以值传递,也可通过ref、out关键字修饰,进行引用传递。引用类型参数只能以引用传递方式传递。

     值传递是将变量的一个副本传递到方法中,方法中如何操作该变量副本,都不会改变原变量的值。引用传递是将变量的内存地址传递给方法,方法操作变量时会找到保存在该地址的变量,对其进行操作。会对原变量造成影响。

7、java有没有多继承,接口和抽象类区别 

    没有多继承。

    总结几句话来说:

   1、抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
   2、抽象类要被子类继承,接口要被类实现。
   3、接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现
   4、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
   5、抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。
   6、抽象方法只能申明,不能实现,接口是设计的结果 ,抽象类是重构的结果
   7、抽象类里可以没有抽象方法
   8、如果一个类里有抽象方法,那么这个类只能是抽象类
   9、抽象方法要被实现,所以不能是静态的,也不能是私有的。
   10、接口可继承接口,并可多继承接口,但类只能单根继承。

   1.抽象类 和 接口 都是用来抽象具体对象的. 但是接口的抽象级别最高
   2.抽象类可以有具体的方法 和属性,  接口只能有抽象方法和不可变常量
   3.抽象类主要用来抽象类别,接口主要用来抽象功能.
   4、抽象类中,且不包含任何实现,派生类必须覆盖它们。接口中所有方法都必须是未实现的。

8、jdk和jre的区别 

      JDK(Java Development Kit)是针对Java开发员的产品,是整个Java的核心,包括了Java运行环境JRE、Java工具和Java基础类库。Java Runtime Environment(JRE)是运行JAVA程序所必须的环境的集合,包含JVM标准实现及Java核心类库。JVM是Java Virtual Machine(Java虚拟机)的缩写,是整个java实现跨平台的最核心的部分,能够运行以Java语言写作的软件程序。

9、讲述一下并发编程与多线程

      并发编程又叫多线程编程。

    1、线程的状态

    1.1创建 线程 的两种方式,接口和线程类。利用接口的好处:更好的体现面向对象的思想,可以避免由于Java的单继承特性而带来的局限;增强程序的健壮性,代码能够被多个线程共享,代码与数据是独立的;(同步问题)适合多个相同程序代码的线程区处理同一资源的情况。
    1.2线程就绪等待调度运行start()方法。
    1.3线程的中断
         这里需要注意的是,如果只是单纯的调用interrupt()方法,线程并没有实际被中断,会继续往下执行。
    1.4、线程挂起和恢复(挂起还拥有对象锁,死锁)
            线程的挂起和恢复实现的正确方法是:通过设置标志位,让线程在安全的位置挂起
    1.5 利用多线程模拟同步运行用jion方法,mThread.jion()表示该线程运行完毕后,在运行调用它的线程。
    1.6stop线程停止
         stop方法突然终止线程(持有这些锁必定有某种合适的理由——也许是阻止其他线程访问尚未处于一致性状态的数据,
突然释放锁可能使某些对象中的数据处于不一致状态)
    1.7线程可以阻塞于四种状态:
          当线程执行Thread.sleep()时,不释放对象锁,它一直阻塞到指定的毫秒时间之后,或者阻塞被另一个线程打断;
          当线程碰到一条wait()语句时,释放对象锁,它会一直阻塞到接到通知(notify())、被中断或经过了指定毫秒时间为止(若制定了超时值的话)
线程阻塞与不同I/O的方式有多种。常见的一种方式是InputStream的read()方法,该方法一直阻塞到从流中读取一个字节的数据为止,它可以无限阻塞,因此不能指定超时时间,线程也可以阻塞等待获取某个对象锁的排他性访问权限(即等待获得synchronized语句必须的锁时阻塞)。
    2、线程的种类
        守护线程与线程阻塞的四种情况
          Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程)
          用户可以用Thread的setDaemon(true)方法设置当前线程为守护线程。守护线程是否已经完成了预期的服务任务。一旦所有的用户线程退出了,虚拟机也就退出运行了。因此,不要在守护线程中执行业务逻辑操作(比如对数据的读写等)。setDaemon(true)必须在调用线程的start()方法之前设置,否则会跑出IllegalThreadStateException异常。在守护线程中产生的新线程也是守护线程。不要认为所有的应用都可以分配给守护线程来进行服务,比如读写操作或者计算逻辑。

10、String、StringBuffer和StringBuilder的区别 

       1.三者在执行速度方面的比较:StringBuilder >  StringBuffer  >  String,StringBuffer与StringBuilder是字符串变量,是可改变的对象,每当我们用它们对字符串做操作时,实际上是在一个对象上操作的,不会像String一样创建副本对象进行操作,当然速度就快了。

       2.StringBuilder:线程非安全的;StringBuffer:线程安全的
         当我们在字符串缓冲去被多个线程使用是,JVM不能保证StringBuilder的操作是安全的,虽然他的速度最快,但是可以保证StringBuffer是可以正确操作的。当然大多数情况下就是我们是在单线程下进行的操作,所以大多数情况下是建议用StringBuilder而不用StringBuffer的,就是速度的原因。

          对于三者使用的总结:   1.如果要操作少量的数据用 = String
                       2.单线程操作字符串缓冲区 下操作大量数据 = StringBuilder
                       3.多线程操作字符串缓冲区 下操作大量数据 = StringBuffer

11、int在32位和64位机中的区别 

      理论上来讲 我觉得数据类型的字节数应该是由CPU决定的,但是实际上主要由编译器决定(占多少位由编译器在编译期间说了算)。

       32位编译器:
      char :1个字节
      char*(即指针变量): 4个字节(32位的寻址空间是2^32, 即32个bit,也就是4个字节。同理64位编译器)++++++
      short int : 2个字节
      int:  4个字节
      unsigned int : 4个字节
      float:  4个字节
      double:   8个字节
      long:   4个字节++++++++
      long long:  8个字节
      unsigned long:  4个字节+++++++++
  64位编译器:
      char :1个字节
      char*(即指针变量): 8个字节++++++++
      short int : 2个字节
      int:  4个字节
      unsigned int : 4个字节
      float:  4个字节
      double:   8个字节
      long:   8个字节++++++++
      long long:  8个字节
      unsigned long:  8个字节++++++++

12、java中基本类型的位数

   Int 32 4  147,483,648 ~ 2,147,483,647
   Short 16   -32768 ~ 32678
   long 64   -9,223,372,036,854,775,808~+9,223,372,036,854,775,807
   float 32   -3,40292347E+38 ~ +3,40292347E+38
   double 64   -1.79769313486231576E+308 ~ 1.79769313486231576E+308
   char 16   ‘\u0000′ ~ ‘\uFFFF’
   boolean 1    true/false
   byte 8    -128 ~ 127

13、static关键字的用法

       用法:是一个修饰符,用于修饰成员(成员变量,成员函数) 

       static特点
       1、随着类的加载而加载也就是说:静态会随着类的消失而消失,说明他的生命周期最长
       2、优先于对象存在
       3、被所有对象所共享
       4、可以直接被类名调用

       →int age(成员变量)实例变量和 static int age(静态成员变量)类变量的区别
      1、存放为着
           实例变量是随着对象的创建而存在堆内存中
           类变量随着类的加载而存在于方法区中
       2、生命周期
            实例变量随着对象的消失而消失
            类变量生命周期最长,随着类的消失而消失
      →静态使用注意事项
      1、静态方法只能访问静态成员
       2、静态方法中不可以定义this,super关键字因为静态优先于对象存在,所以静态方法中不可以出现this
       3、主函数是静态的
     →静态的好处和坏处
          好处:对对象的数据进行单独空间的存储,节省空间,没有必要每个对象中的存储一份可以直接被类名调用(Person.country)
          坏处:生命周期过长。
                     访问出现局限性。(只能访问静态)

14、java的访问权限,成员的可见性

这里写图片描述 

15、HashmMap、HashTable、ConcurrentHashMap的区别与联系 

      HashTable和HashMap采用相同的存储机制,二者的实现基本一致,不同的是:
      1、HashMap是非线程安全的,HashTable是线程安全的,内部的方法基本都是synchronized。
       2、HashTable不允许有null值的存在。
            在HashTable中调用put方法时,如果key为null,直接抛出NullPointerException。其它细微的差别还有,比如初始化Entry数组的大小等等,但基本思想和HashMap一样。
      3、因为线程安全的问题,HashMap效率比HashTable的要高。

      ConcurrentHashMap是线程安全的HashMap的实现。之前我们说,synchronized关键字加锁的原理,其实是对对象加锁,不论你是在方法前加synchronized还是语句块前加,锁住的都是对象整体,但是ConcurrentHashMap的同步机制和这个不同,它不是加synchronized关键字,而是基于lock操作的,这样的目的是保证同步的时候,锁住的不是整个对象。事实上,ConcurrentHashMap可以满足concurrentLevel个线程并发无阻塞的操作集合对象。

16、char能否存储汉字? 

       char 在java中是2个字节。所以可以存储中文

17、String转成int型,能不能转,怎么转

       在 Java 中要将 String 类型转化为 int 类型时,需要使用 Integer 类中的 parseInt() 方法或者 valueOf() 方法进行转换. 

18、HashMap和HashSet的实现原理 

       对于HashMap及其子类而言,它们采用Hash算法来决定集合中元素的存储位置。当系统开始初始化HashMap时,系统会创建一个长度为capacity的Entry数组,这个数组里可以存储元素的位置被称为“桶(bucket)”,每个bucket都有其指定索引,系统可以根据其索引快速访问该bucket里存储的元素。当每个bucket只存储一个元素时,HashMap性能最好。当解决冲突而产生的链越长,性能越差。装填因子load factor,默认值是0.75,这个是空间和时间的折衷,增大装填因子,可以减小Hash表所占用的空间,但会增加查找时间,减小装填因子,会提高数据查询性能,但会增加Hash表所占用的内存空间。在new 一个hashMap的时候,可以适当的传入要建立的大小,传入的应该是2的n次幂。

     HashSet底层是通过HashMap实现的。

19、动态代理的原理

       java动态代理的类和接口:

         1,java.lang.reflect.Proxy:动态代理机制的主类,提供一组静态方法为一组接口动态的生成对象和代理类。

         2,java.lang.reflect.InvocationHandler:调用处理器接口,自定义invokle方法,用于实现对于真正委托类的代理访问

         3,java.lang.ClassLoader:类装载器类,将类的字节码装载到 Java 虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。Proxy类与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中。 每次生成动态代理类对象时都需要指定一个类装载器对象:newProxyInstance()方法第一个参数.

      java动态代理创建对象的过程为如下步骤: 
          1,通过实现 InvocationHandler 接口创建自己的调用处理器;
          2,通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
          3,通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
          4,通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
         为了简化对象创建过程,Proxy类中的newProxyInstance方法封装了2~4,只需两步即可完成代理对象的创建。

20、包装类型和基本数据类型的比较问题(例如:Integer能否 == int类型变量,能否做比较,什么时候不能做比较) 

      包装类创建的是对象,拥有方法和字段.对象的调用都是通过引用对象的地址 ;

      基本类型不是.另外一个区别是,包装类是引用传递 而基本类型是值传递 ;
      变量的值存储在栈里,而对象存储在堆里,相比而言,堆栈更高效,这也是java保留基本类型的原因。包装类创建的对象,可以使用api提供的一些有用的方法。更为强大。

21、ArrayList和LinkedList的比较

       1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。 
       2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。 
       3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。 

22、单例模式都有什么,是否线程安全,怎么改进(从synchronized到双重检验锁到枚举) 

     第一种(懒汉,线程不安全):
           public class Singleton {  
                private static Singleton instance;  
                private Singleton (){}  
                public static Singleton getInstance() {  
                      if (instance == null) {  
                              instance = new Singleton();  
                      }  
                              return instance;  
                 }  
             }  
     第二种(懒汉,线程安全):
          public class Singleton {  
              private static Singleton instance;  
              private Singleton (){}  
              public static synchronized Singleton getInstance() {  
                   if (instance == null) {  
                          instance = new Singleton();  
                    }  
              return instance;  
              }  
         }  

23、java线程安全都体现在哪些方面,如何维护线程安全?

      在Java里,线程安全一般体现在两个方面: 
  (1)多个thread对同一个java实例的访问(read和modify)不会相互干扰,它主要体现在关键字synchronized.如ArrayList和Vector,HashMap和Hashtable(后者每个方法前都有synchronized关键字)。如果你在Iterator遍历一个List对象时,同时其它线程remove一个element时,就会出现问题,数据丢失。(ArrayList是非线程安全的,而Vector是线程安全的;HashMap是非线程安全的,而Hashtable是线程安全的。) 
  (2)每个线程都有自己的私有字段或变量,这样可以不会在多个线程之间共享。它主要体现在java.lang.ThreadLocal类,而没有Java关键字支持,如像static、transient那样。(一个对象只要实现了Serilizable接口,这个对象就可以被序列化,java的这种序列化模式为开发者提供了很多便利,我们可以不必关系具体序列化的过程,只要这个类实现了Serilizable接口,这个对象的所有属性和方法都会自动序列化。 然而在实际开发过程中,某些类的有些属性需要序列化,而其他属性不需要被序列化时,则可以借助java的transient关键字。在需要序列化对象的时候,实现Serilizable接口,将类中不需要序列化的属性前添加关键字transient,那么带transient关键字的属性就不会序列化到指定的目的地中。)

24、反射机制中可以获取private成员的值吗(没有set和get函数)

       利用反射,首先是Class对象的获取,之后是Method和Field对象的获取。
  以Method为例,从文档中可以看到:
       getMethod()方法返回的是public的Method对象,而getDeclaredMethod()返回的Method对象可以是非public的。
  Field的方法同理。
  访问私有属性和方法,在使用前要通过AccessibleObject类(Constructor、 Field和Method类的基类)中的setAccessible()方法来抑制Java访问权限的检查。

25、写一段synchronized可能发生死锁的代码 

     死锁是两个或更多线程阻塞着等待其它处于死锁状态的线程所持有的锁。死锁通常发生在多个线程同时但以不同的顺序请求同一组锁的时候。
     例如,如果线程1锁住了A,然后尝试对B进行加锁,同时线程2已经锁住了B,接着尝试对A进行加锁,这时死锁就发生了。线程1永远得不到B,线程2也永远得不到A,并且它们永远也不会知道发生了这样的事情。为了得到彼此的对象(A和B),它们将永远阻塞下去。这种情况就是一个死锁。

26、死锁的条件,摒弃死锁的条件有哪些?

    1.产生死锁的原因:
     (1)竞争不可抢占性资源。
     (2)竞争可消耗资源。

      (3)进程推进顺序不当。

    2.产生死锁的四个必要条件:
      (1) 互斥条件:一个资源每次只能被一个进程使用。
      (2) 请求和保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
      (3) 不可抢占条件:进程已获得的资源,在末使用完之前,不能强行剥夺,只能在进程使用完时由自己释放。
      (4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
    这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。因此可以写下如下的预防死锁的方法。

    3.避免死锁的方法:
   (1)破坏“互斥”条件:就是在系统里取消互斥。若资源不被一个进程独占使用,那么死锁是肯定不会发生的。但一般“互斥”条件是无法破坏的。因此,在死锁预防里主要是破坏其他三个必要条件,而不去涉及破坏“互斥”条件。
   (2)破坏“请求和保持”条件:在系统中不允许进程在已获得某种资源的情况下,申请其他资源。即要想出一个办法,阻止进程在持有资源的同时申请其他资源。
           方法一:所有进程在运行之前,必须一次性地申请在整个运行过程中所需的全部资源。这样,该进程在整个运行期间,便不会再提出资源请求,从而破坏了“请求”条件。系统在分配资源时,只要有一种资源不能满足进程的要求,即使其它所需的各资源都空闲也不分配给该进程,而让该进程等待。由于该进程在等待期间未占有任何资源,于是破坏了“保持”条件。

           方法二:要求每个进程提出新的资源申请前,释放它所占有的资源。这样,一个进程在需要资源S时,须先把它先前占有的资源R释放掉,然后才能提出对S的申请,即使它可能很快又要用到资源R。
                    该方法优点:简单、易行且安全。
                    缺点:a.资源被严重浪费,严重恶化了资源的利用率。

   (3)破坏“不可抢占”条件:允许对资源实行抢夺。 
           方法一:如果占有某些资源的一个进程进行进一步资源请求被拒绝,则该进程必须释放它最初占有的资源,如果有必要,可再次请求这些资源和另外的资源。 
           方法二:如果一个进程请求当前被另一个进程占有的一个资源,则操作系统可以抢占另一个进程,要求它释放资源。只有在任意两个进程的优先级都不相同的条件下,该方法才能预防死锁。
   (4)破坏“循环等待”条件:将系统中的所有资源统一编号,进程可在任何时刻提出资源申请,但所有申请必须按照资源的编号顺序(升序)提出。这样做就能保证系统不出现死锁。
    利用银行家算法避免死锁。

   4.死锁的解除:
      一旦检测出死锁,就应立即釆取相应的措施,以解除死锁。死锁解除的主要两种方法:
             1) 抢占资源。从一个或多个进程中抢占足够数量的资源,分配给死锁进程,以解除死锁状态。
             2) 终止(或撤销)进程。终止(或撤销)系统中的一个或多个死锁进程,直至打破循环环路,使系统从死锁状态解脱出来

二、关于java虚拟机 
1、垃圾回收算法,为什么要分代处理 

     java垃圾回收算法实现原理,有两种,一个是引用计数法,一个是引用可达法。 
     引用计数法,每个对象有一个专门的空间维护一个引用计数器,当该对象被引用时,计数器加一,引用消失时则计数器减一,当该对象引用数为0时则回收该对象。这个算法有一个好处就是可以及时回收废弃的对象,而不用等到空间占满后在统一回收,但是其无法解决循环引用的情况。 
     而引用可达性分析法,则是选取一个root节点,通过该节点是否能够遍历到某个对象的方法来判断该对象是否被回收。root节点的选择通常是静态变量,native变量,常量等。 
垃圾回收算法有三种 
     1.标记清除 
         也就是每次GC,会先扫描内存区标记存活对象,而后释放未被标记的对象空间。这种算法在工作时需要停止工作线程,再进行标记清除,同时会产生内存碎片,而且jvm需要维护一个内存空间表,用于分配内存空间。 
     2.复制清除 
         将内存区分为两个相同大小的区域,先在A上分配对象,当其分配满后,进行标记操作,而后将标记对象按照内存顺序复制到B区域中,最后将A区域全部清除。这种方式可以有效避免内存碎片的问题,但是复制操作会产生额外的耗时,当存活对象多时并不适合使用该算法,因为复制所产生的耗时会很长。因此这种算法适合对象存活时间短的情况。同时会造成一定的内存浪费。 
      3.标记整理 
         这种算法就是在标记清除的基础上为了避免内存碎片的产生而优化产生的算法。总的过程分为两步,分别是标记和整理。
       java内存模型将内存区域分为,新生代、老年代、永久代。 
      新生代由于其对象存活时间短,且需要经常gc,因此采用效率较高的复制算法,其将内存区分为一个eden区和两个suvivor区,eden区和survivor区的比例是8:1,分配内存时先分配eden区,当eden区满时,使用复制算法进行gc,将存活对象复制到一个survivor区,当一个survivor区满时,将其存活对象复制到另一个区中,当对象存活时间大于某一阈值时,将其放入老年代。 
老年代和永久代因为其存活对象时间长,因此使用标记清除或标记整理算法
2、GC停顿原因,如何降低停顿 

     垃圾收集器长时间停顿,表现在 Web 页面上可能是页面响应码 500 之类的服务器错误问题,如果是个支付过程可能会导致支付失败,将造成公司的直接经济损失,程序员要尽量避免或者说减少此类情况发生。

     1)并发模式失败

       两个原因:
          在 CMS 收集器启动过程中,新生代提升速度过快,老年代收集速度赶不上新生代提升速度
          在 CMS 启动过程中,老年代碎片化严重,无法容纳新生代提升上来的大对象
      发送这种情况,应用线程将会全部停止(相当于网站这段时间无法响应用户请求),进行压缩式垃圾收集(回退到 Serial Old 算法)
      解决办法:
          新生代提升过快问题:(1)如果频率太快的话,说明空间不足,首先可以尝试调大新生代空间和晋升阈值。(2)如果内存有限,可以设置 CMS 垃圾收集在老年代占比达到多少时启动来减少问题发生频率(越早启动问题发生频率越低,但是会降低吞吐量,具体得多调整几次找到平衡点),参数如下:如果没有第二个参数,会随着 JVM 动态调节 CMS 启动时间

            -XX:CMSInitiatingOccupancyFraction=68 (默认是 68)
            -XX:+UseCMSInitiatingOccupancyOnly
          老年代碎片严重问题:(1)如果频率太快或者 Full GC 后空间释放不多的话,说明空间不足,首先可以尝试调大老年代空间(2)如果内存不足,可以设置进行 n 次 CMS 后进行一次压缩式 Full GC,参数如下:
            -XX:+UseCMSCompactAtFullCollection:允许在 Full GC 时,启用压缩式 GC
            -XX:CMSFullGCBeforeCompaction=n     在进行 n 次,CMS 后,进行一次压缩的 Full GC,用以减少 CMS 产生的碎片

     2)提升失败

         在 Minor GC 过程中,Survivor Unused 可能不足以容纳 Eden 和另一个 Survivor 中的存活对象, 那么多余的将被移到老年代, 称为过早提升(Premature Promotion)。 这会导致老年代中短期存活对象的增长, 可能会引发严重的性能问题。  再进一步, 如果老年代满了, Minor GC 后会进行 Full GC, 这将导致遍历整个堆, 称为提升失败(Promotion Failure)。

       提升失败原因:Minor GC 时发现 Survivor 空间放不下,而老年代的空闲也不够新生代提升太快
                              老年代碎片太多,放不下大对象提升(表现为老年代还有很多空间但是,出现了 promotion failed)
      解决方法:
               两条和上面 concurrent mode failure 一样
               另一条,是因为 Survivor Unused 不足,那么可以尝试调大 Survivor 来尝试下 

     3)在 GC 的时候其他系统活动影响

         有些时候系统活动诸如内存换入换出(vmstat)、网络活动(netstat)、I/O (iostat)在 GC 过程中发生会使 GC 时间变长。

        解决方法:减少线程,这样可以降低内存换入换出;增加内存;如果是 JVM 内存设置过大导致线程所用内存不足,则适当调低 -Xmx 和 -Xms。

3、JVM如何调优,参数怎么调 

    (1)参数
-Xms:初始堆大小
-Xmx :最大堆大小 此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存
-Xmn :年轻代大小 整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
-XX:NewSize:设置年轻代大小
-XX:MaxNewSize:年轻代最大值
-XX:NewRatio 年老代与年轻代的比值
-XX:SurvivorRatio:设置年轻代中Eden区与Survivor区的大小比值
-XX:MaxTenuringThreshold:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象在年轻代的存活时间,增加在年轻代即被回收的概论。
-XX:PermSize:设置持久带
-XX:MaxPermSize:设置持久代最大值
(2)调优
JVM调优主要是针对内存管理方面的调优,包括控制各个代的大小,GC策略。由于GC开始垃圾回收时会挂起应用线程,严重影响了性能,调优的目是为了尽量降低GC所导致的应用线程暂停时间、 减少Full GC次数。
关键参数:-Xms -Xmx 、-Xmn 、-XX:SurvivorRatio、-XX:MaxTenuringThreshold、-XX:PermSize、-XX:MaxPermSize
(1)-Xms、 -Xmx 通常设置为相同的值,避免运行时要不断扩展JVM内存,这个值决定了JVM heap所能使用的最大内存。
(2)-Xmn 决定了新生代空间的大小,新生代Eden、S0、S1三个区域的比率可以通过-XX:SurvivorRatio来控制(假如值为 4  表示:Eden:S0:S1 = 4:3:3 )
(3)-XX:MaxTenuringThreshold 控制对象在经过多少次minor GC之后进入老年代,此参数只有在Serial 串行GC时有效。
(4)-XX:PermSize、-XX:MaxPermSize 用来控制方法区的大小,通常设置为相同的值。
 
(1)代调优
   合理设置新生代大小
    1)避免新生代大小设置过小
当新生代设置过小时,会产生两种比较明显的现象,一是minor GC次数频繁,二是可能导致 minor GC对象直接进入老年代。当老年代内存不足时,会触发Full GC。
    2)避免新生代设置过大
新生代设置过大,会带来两个问题:一是老年大变小,可能导致Full  GC频繁执行;二是 minor GC 执行回收的时间大幅度增加。
年轻代大小选择
响应时间优先的应用:尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择)。在此种情况下,年轻代收集发生的频率也是最小的。同时,减少到达年老代的对象。
吞吐量优先的应用:尽可能的设置大,可能到达Gbit的程度。因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8CPU以上的应用。
年老代大小选择
响应时间优先的应用:年老代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数。如果堆设置小了,可以会造成内存碎片、高回收频率以及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间。最优化的方案,一般需要参考以下数据获得:
并发垃圾收集信息
持久代并发收集次数
传统GC信息
花在年轻代和年老代回收上的时间比例
减少年轻代和年老代花费的时间,一般会提高应用的效率
吞吐量优先的应用:一般吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代。原因是,这样可以尽可能回收掉大部分短期对象,减少中期的对象,而年老代尽存放长期存活对象。
    合理设置Survivor区
  -XX:SurvivorRatio参数的值越大,就意味着Eden区域变大,minor GC次数会降低,但两块Survivor区域变小,如果超过Survivor区域内存大小的对象在minor GC后仍没被回收,则会直接进入老年代,
-XX:SurvivorRatio参数值设置过小,就意味着Eden区域变小,minor GC触发次数会增加,Survivor区域变大,意味着可以存储更多在minor GC后任存活的对象,避免其进入老年代。
  合理设置对象在新生代的存活时间
新生代存活周期的值决定了新生代对象在经过多少次Minor GC后进入老年代。因此这个值要根据自己的应用来调优,Jvm参数上这个值对应的为-XX:MaxTenuringThreshold,默认值为15次。
 初始堆大小和最大堆大小相同
-XX:PermSize、-XX:MaxPermSize 用来控制方法区的大小,通常设置为相同的值。避免运行时要不断扩展JVM内存,这个值决定了JVM heap所能使用的最大内存。
 
较小堆引起的碎片问题
因为年老代的并发收集器使用标记、清除算法,所以不会对堆进行压缩。当收集器回收时,他会把相邻的空间进行合并,这样可以分配给较大的对象。但是,当堆空间较小时,运行一段时间以后,就会出现“碎片”,如果并发收集器找不到足够的空间,那么并发收集器将会停止,然后使用传统的标记、清除方式进行回收。如果出现“碎片”,可能需要进行如下配置:
-XX:+UseCMSCompactAtFullCollection:使用并发收集器时,开启对年老代的压缩。
-XX:CMSFullGCsBeforeCompaction=0:上面配置开启的情况下,这里设置多少次Full GC后,对年老代进行压缩
(2)GC策略调优
(1)合理选择垃圾收集器的搭配使用
(2)使用可视化工具JConsole查看JVM参数
JConsole工具在JDK/bin目录下,启动JConsole后,将自动搜索本机运行的jvm进程,不需要jps命令来查询指定。双击其中一个jvm进程即可开始监控,也可使用“远程进程”来连接远程服务器。
4、如何使用工具分析jvm状态 

       1. 输出GC日志

       2. GC Portal
  将GC日志输出固然有一定的作用,但如果要靠人为进行分析,还是相当复杂的。因此Sun提供了一个GC Portal来帮助分析这些GC日志,并生成相关的图形化的报表,GC Portal部署起来会有些麻烦,它需要运行在老版本的Tomcat上,同时需要数据库,部署完毕后通过上传日志文件的方式即可完成GC日志的分析,此GC日志输出的JVM参数为:-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps [-Xloggc:文件名],在上传日志时GC Portal的选项里只有jdk 1.2或jdk 1.2-1.4的版本。虽然经过测试,JDK 6的日志也是可以分析出来的,但它的限制在于仅支持5MB的gc日志的分析,GC Portal可提供吞吐量的分析、耗费的CPU的时间、造成的应用暂停的时间、每秒从新生代转化到旧生代的数量、minor GC的状况及Full GC的状况等

        3. JConsole
  JConsole可以图形化查看JVM中内存的变化状况,JConsole是JDK 5及以上版本中自带的工具,位于JDK的bin目录下,运行时直接运行JConsole.exe或JConsole.sh(要求支持图形界面)。在本地的Tab页上看到运行了java的pid,双击即可查看相应进程的JVM状况,同时,JConsole也支持查看远程的JVM的运行状况,具体可参见JConsole的User Guide。
  JConsole中显示了JVM中很多的信息:内存、线程、类和MBean等,在打开JConsole的内存Tab页后,可看到JVM内存部分的运行状况。

        4. JVisualVM
  JVisualVM是JDK 6 update 7之后推出的一个工具,它类似于JProfiler的工具,基于此工具可查看内存的消耗情况、线程的执行状况及程序中消耗CPU、内存的动作。
  在内存分析上,JVisualVM的最大好处是可通过安装VisualGC插件来分析GC趋势、内存消耗详细状况。

        5. JMap
  JMap是JDK中自带的一个用于分析JVM内存状况的工具,位于JDK的bin目录下。使用JMap可查看目前JVM中各个代的内存状况、JVM中对象的内存的占用状况,以及导出整个JVM中的内存信息。

        6. JHat
  JHat是Sun JDK 6及以上版本中自带的一个用于分析jvm堆dump文件的工具,基于此工具可分析jvm heap中对象的内存占用状况、引用关系等。

        7. JStat
  JStat是Sun JDK自带的一个统计分析JVM运行状况的工具,位于JDK的bin目录下,除了可用于分析GC的状况外,还可用于分析编译的状况、class加载的状况等。

        8. Eclipse Memory Analyzer
  Eclipse Memory Analyzer是Eclipse提供的一个用于分析jvm堆dump文件的插件,借助这个插件可查看对象的内存占用状况、引用关系、分析内存泄露等。

5、类加载机制 

     JAVA源码编译由三个过程组成:
       1、源码编译机制。
       2、类加载机制
       3、类执行机制
       我们这里主要介绍编译和类加载这两种机制。
       一、源码编译
             代码编译由JAVA源码编译器来完成。主要是将源码编译成字节码文件(class文件)。字节码文件格式主要分为两部分:常量池和方法字节码。
       二、类加载
             类的生命周期是从被加载到虚拟机内存中开始,到卸载出内存结束。过程共有七个阶段,其中到初始化之前的都是属于类加载的部分
            加载----验证----准备----解析-----初始化----使用-----卸载
系统可能在第一次使用某个类时加载该类,也可能采用预加载机制来加载某个类,当运行某个java程序时,会启动一个java虚拟机进程,两次运行的java程序处于两个不同的JVM进程中,两个jvm之间并不会共享数据。
      1、加载阶段
        这个流程中的加载是类加载机制中的一个阶段,这两个概念不要混淆,这个阶段需要完成的事情有:
               1)通过一个类的全限定名来获取定义此类的二进制字节流。
               2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
               3)在java堆中生成一个代表这个类的Class对象,作为访问方法区中这些数据的入口。
            由于第一点没有指明从哪里获取以及怎样获取类的二进制字节流,所以这一块区域留给我开发者很大的发挥空间。这个我在后面的类加载器中在进行介绍。
      2、准备阶段
          这个阶段正式为类变量(被static修饰的变量)分配内存并设置类变量初始值,这个内存分配是发生在方法区中。
              1、注意这里并没有对实例变量进行内存分配,实例变量将会在对象实例化时随着对象一起分配在JAVA堆中。
              2、这里设置的初始值,通常是指数据类型的零值。

     3、初始化阶段
            初始化是类加载机制的最后一步,这个时候才正真开始执行类中定义的JAVA程序代码。在前面准备阶段,类变量已经赋过一次系统要求的初始值,在初始化阶段最重要的事情就是对类变量进行初始化,关注的重点是父子类之间各类资源初始化的顺序。
           java类中对类变量指定初始值有两种方式:1、声明类变量时指定初始值;2、使用静态初始化块为类变量指定初始值。

       初始化的步骤
          1、如果该类还没有加载和连接,则程序先加载该类并连接。
          2、如果该类的直接父类没有加载,则先初始化其直接父类。
          3、如果类中有初始化语句,则系统依次执行这些初始化语句。
          在第二个步骤中,如果直接父类又有直接父类,则系统会再次重复这三个步骤来初始化这个父类,依次类推,JVM最先初始化的总是java.lang.Object类。当程序主动使用任何一个类时,系统会保证该类以及所有的父类都会被初始化。
6、什么是java虚拟机,对jvm的理解 

1.Java语言运行的过程

Java语言写的源程序通过Java编译器,编译成与平台无关的‘字节码程序’(.class文件,也就是0,1二进制程序),然后在OS之上的Java解释器中解释执行。

Java语言运行的过程

也相当与

Java语言运行的过程

注:JVM(java虚拟机)包括解释器,不同的JDK虚拟机是相同的,解释器不同。

2.JVM:

JVM是java的核心和基础,在java编译器和os平台之间的虚拟处理器。它是一种利用软件方法实现的抽象的计算机基于下层的操作系统和硬件平台,可以在上面执行java的字节码程序。

java编译器只要面向JVM,生成JVM能理解的代码或字节码文件。Java源文件经编译成字节码程序,通过JVM将每一条指令翻译成不同平台机器码,通过特定平台运行。

JVM执行程序的过程 :

I.加载。class文件

II.管理并分配内存

III.执行垃圾收集

JRE(java运行时环境)由JVM构造的java程序的运行环境 

JVM执行程序的过程


7、jvm内存模型,各个区的作用 

Java虚拟机在程序执行过程会把jvm的内存分为若干个不同的数据区域来管理,这些区域有自己的用途,以及创建和销毁时间。 
jvm管理的内存区域包括以下几个区域: 
这里写图片描述

栈区: 
栈分为java虚拟机栈和本地方法栈

  • 重点是Java虚拟机栈,它是线程私有的,生命周期与线程相同。
  • 每个方法执行都会创建一个栈帧,用于存放局部变量表,操作栈,动态链接,方法出口等。每个方法从被调用,直到被执行完。对应着一个栈帧在虚拟机中从入栈到出栈的过程。
  • 通常说的栈就是指局部变量表部分,存放编译期间可知的8种基本数据类型,及对象引用和指令地址。局部变量表是在编译期间完成分配,当进入一个方法时,这个栈中的局部变量分配内存大小是确定的。
  • 会有两种异常StackOverFlowError和 OutOfMemoneyError。当线程请求栈深度大于虚拟机所允许的深度就会抛出StackOverFlowError错误;虚拟机栈动态扩展,当扩展无法申请到足够的内存空间时候,抛出OutOfMemoneyError。
  • 本地方法栈 为虚拟机使用到本地方法服务(native)

堆区:

  • 堆被所有线程共享区域,在虚拟机启动时创建,唯一目的存放对象实例
  • 堆区是gc的主要区域,通常情况下分为两个区块年轻代和年老代。更细一点年轻代又分为Eden区最要放新创建对象,From survivor 和 To survivor 保存gc后幸存下的对象,默认情况下各自占比 8:1:1。 
    不过很多文章介绍分为3个区块,把方法区算着为永久代。这大概是基于Hotspot虚拟机划分, 然后比如IBM j9就不存在永久代概论。不管怎么分区,都是存放对象实例。
  • 会有异常OutOfMemoneyError

方法区:

  • 被所有线程共享区域,用于存放已被虚拟机加载的类信息,常量,静态变量等数据。被Java虚拟机描述为堆的一个逻辑部分。习惯是也叫它永久代(permanment generation)
  • 垃圾回收很少光顾这个区域,不过也是需要回收的,主要针对常量池回收,类型卸载。
  • 常量池用于存放编译期生成的各种字节码和符号引用,常量池具有一定的动态性,里面可以存放编译期生成的常量;运行期间的常量也可以添加进入常量池中,比如string的intern()方法。

程序计数器

  • 当前线程所执行的行号指示器。通过改变计数器的值来确定下一条指令,比如循环,分支,跳转,异常处理,线程恢复等都是依赖计数器来完成。
  • Java虚拟机多线程是通过线程轮流切换并分配处理器执行时间的方式实现的。为了线程切换能恢复到正确的位置,每条线程都需要一个独立的程序计数器,所以它是线程私有的。
  • 唯一一块Java虚拟机没有规定任何OutofMemoryError的区块

jvm分区大致就这个块,具体里面还有很多细节,及其各个模块工作的算法都很复杂,这里只是对分区进行简单介绍,掌握一些基本的知识点。


8、堆的作用 

     从堆和栈的功能和作用来通俗的比较,堆主要用来存放对象的,栈主要是用来执行程序的.

     堆是应用程序在运行的时候请求操作系统分配给自己内存,由于从操作系统管理的内存分配,所以在分配和销毁时都要占用时间,因此用堆的效率非常低.但是堆的优点在于,编译器不必知道要从堆里分配多少存储空间,也不必知道存储的数据要在堆里停留多长的时间,因此,用堆保存数据时会得到更大的灵活性。事实上,面向对象的多态性,堆内存分配是必不可少的,因为多态变量所需的存储空间只有在运行时创建了对象之后才能确定.在C++中,要求创建一个对象时,只需用new命令编制相关的代码即可。执行这些代码时,会在堆里自动进行数据的保存.当然,为达到这种灵活性,必然会付出一定的代价:在堆里分配存储空间时会花掉更长的时间!

     每一个Java应用都唯一对应一个JVM实例,每一个实例唯一对应一个堆。应用程序在运行中所创建的所有类实例或数组都放在这个堆中,并由应用所有的线程共享.跟C/C++不同,Java中分配堆内存是自动初始化的。Java中所有对象的存储空间都是在堆中分配的,但是这个对象的引用却是在堆栈中分配,也就是说在建立一个对象时从两个地方都分配内存,在堆中分配的内存实际建立这个对象,而在堆栈中分配的内存只是一个指向这个堆对象的指针(引用)而已。

      JVM为每个新创建的线程都分配一个堆栈.也就是说,对于一个Java程序来说,它的运行就是通过对堆栈的操作来完成的。堆栈以帧为单位保存线程的状态。JVM对堆栈只进行两种操作:以帧为单位的压栈和出栈操作。
   我们知道,某个线程正在执行的方法称为此线程的当前方法.我们可能不知道,当前方法使用的帧称为当前帧。当线程激活一个Java方法,JVM就会在线程的Java堆栈里新压入一个帧。这个帧自然成为了当前帧.在此方法执行期间,这个帧将用来保存参数,局部变量,中间计算过程和其他数据.这个帧在这里和编译原理中的活动纪录的概念是差不多的.
  从Java的这种分配机制来看,堆栈又可以这样理解:堆栈(Stack)是操作系统在建立某个进程时或者线程(在支持多线程的操作系统中是线程)为这个线程建立的存储区域,该区域具有先进后出的特性。
9、JVM调试工具 
10、堆和栈的区别 
11、内存溢出了怎么办 

      java中三种常见内存溢出错误的处理方法(good)

          相信有一定java开发经验的人或多或少都会遇到OutOfMemoryError的问题,这个问题曾困扰了我很长时间,随着解决各类问题经验的积累以及对问题根源的探索,终于有了一个比较深入的认识。
          在解决java内存溢出问题之前,需要对jvm(java虚拟机)的内存管理有一定的认识。jvm管理的内存大致包括三种不同类型的内存区域:Permanent Generation space(永久保存区域)、Heap space(堆区域)、Java Stacks(Java栈)。其中永久保存区域主要存放Class(类)和Meta的信息,Class第一次被Load的时候被放入PermGen space区域,Class需要存储的内容主要包括方法和静态属性。堆区域用来存放Class的实例(即对象),对象需要存储的内容主要是非静态属性。每次用new创建一个对象实例后,对象实例存储在堆区域中,这部分空间也被jvm的垃圾回收机制管理。而Java栈跟大多数编程语言包括汇编语言的栈功能相似,主要基本类型变量以及方法的输入输出参数。Java程序的每个线程中都有一个独立的堆栈。容易发生内存溢出问题的内存空间包括:Permanent Generation space和Heap space。
    第一种OutOfMemoryError: PermGen space
       发生这种问题的原意是程序中使用了大量的jar或class,使java虚拟机装载类的空间不够,与Permanent Generation space有关。解决这类问题有以下两种办法:
          增加java虚拟机中的XX:PermSize和XX:MaxPermSize参数的大小,其中XX:PermSize是初始永久保存区域大小,XX:MaxPermSize是最大永久保存区域大小。如针对tomcat6.0,在catalina.sh 或catalina.bat文件中一系列环境变量名说明结束处(大约在70行左右) 增加一行: JAVA_OPTS=" -XX:PermSize=64M -XX:MaxPermSize=128m" 如果是windows服务器还可以在系统环境变量中设置。感觉用tomcat发布sprint+struts+hibernate架构的程序时很容易发生这种内存溢出错误。使用上述方法,我成功解决了部署ssh项目的tomcat服务器经常宕机的问题。
          清理应用程序中web-inf/lib下的jar,如果tomcat部署了多个应用,很多应用都使用了相同的jar,可以将共同的jar移到tomcat共同的lib下,减少类的重复加载。这种方法是网上部分人推荐的,我没试过,但感觉减少不了太大的空间,最靠谱的还是第一种方法。
      第二种OutOfMemoryError:  Java heap space
          发生这种问题的原因是java虚拟机创建的对象太多,在进行垃圾回收之间,虚拟机分配的到堆内存空间已经用满了,与Heap space有关。解决这类问题有两种思路:
              检查程序,看是否有死循环或不必要地重复创建大量对象。找到原因后,修改程序和算法。 我以前写一个使用K-Means文本聚类算法对几万条文本记录(每条记录的特征向量大约10来个)进行文本聚类时,由于程序细节上有问题,就导致了Java heap space的内存溢出问题,后来通过修改程序得到了解决。
              增加Java虚拟机中Xms(初始堆大小)和Xmx(最大堆大小)参数的大小。如:set JAVA_OPTS= -Xms256m -Xmx1024m
     第三种OutOfMemoryError:unable to create new native thread
         在java应用中,有时候会出现这样的错误:OutOfMemoryError: unable to create new native thread.这种怪事是因为JVM已经被系统分配了大量的内存(比如1.5G),并且它至少要占用可用内存的一半。有人发现,在线程个数很多的情况下,你分配给JVM的内存越多,那么,上述错误发生的可能性就越大。
        那么是什么原因造成这种问题呢?每一个32位的进程最多可以使用2G的可用内存,因为另外2G被操作系统保留。这里假设使用1.5G给JVM,那么还余下500M可用内存。这500M内存中的一部分必须用于系统dll的加载,那么真正剩下的也许只有400M,现在关键的地方出现了:当你使用Java创建一个线程,在JVM的内存里也会创建一个Thread对象,但是同时也会在操作系统里创建一个真正的物理线程(参考JVM规范),操作系统会在余下的400兆内存里创建这个物理线程,而不是在JVM的1500M的内存堆里创建。在jdk1.4里头,默认的栈大小是256KB,但是在jdk1.5里头,默认的栈大小为1M每线程,因此,在余下400M的可用内存里边我们最多也只能创建400个可用线程。这样结论就出来了,要想创建更多的线程,你必须减少分配给JVM的最大内存。还有一种做法是让JVM宿主在你的JNI代码里边。
给出一个有关能够创建线程的最大个数的估算公式:
       (MaxProcessMemory - JVMMemory - ReservedOsMemory) / (ThreadStackSize) = Number of threads
对于jdk1.5而言,假设操作系统保留120M内存:
1.5GB JVM: (2GB-1.5Gb-120MB)/(1MB) = ~380 threads
1.0GB JVM: (2GB-1.0Gb-120MB)/(1MB) = ~880 threads
对于栈大小为256KB的jdk1.4而言,
1.5GB allocated to JVM: ~1520 threads
1.0GB allocated to JVM: ~3520 threads 
对于这个异常我们首先需要判断下,发生内存溢出时进程中到底都有什么样的线程,这些线程是否是应该存在的,是否可以通过优化来降低线程数; 另外一方面默认情况下java为每个线程分配的栈内存大小是1M,通常情况下,这1M的栈内存空间是足足够用了,因为在通常在栈上存放的只是基础类型的数据或者对象的引用,这些东西都不会占据太大的内存, 我们可以通过调整jvm参数,降低为每个线程分配的栈内存大小来解决问题,例如在jvm参数中添加-Xss128k将线程栈内存大小设置为128k。

12、什么时候会栈溢出

        栈(JVM Stack)存放主要是栈帧( 局部变量表, 操作数栈 , 动态链接 , 方法出口信息 )的地方。注意区分栈和栈帧:栈里包含栈帧。
       与线程栈相关的内存异常有两个:
            a)、StackOverflowError(方法调用层次太深,内存不够新建栈帧)
            b)、OutOfMemoryError(线程太多,内存不够新建线程)
       栈溢出抛出java.lang.StackOverflowError错误,出现此种情况是因为方法运行的时候,请求新建栈帧时,栈所剩空间小于战帧所需空间。例如,通过递归调用方法,不停的产生栈帧,一直把栈空间堆满,直到抛出异常 

三、关于java后台 
1、介绍Spring的IOC和AOP,分别是如何实现的(反射机制和动态代理) 
2、redis如何处理分布式服务器并发造成的不一致,如果数据库不提供隔离呢? 

    (一种思路) 首先redis是作为缓存的,一般作为缓存有两种用途,快速访问和减少IO频率,所谓减少IO频率就是等缓存积累到一定大小然后一次刷入磁盘进行持久化。一般的设计就是客户端往数据库里更新或者写读数据,redis做为经常需要被读取的数据或者被修改数据的缓存,提高操作效率,一般的操作应该是客户端要修改数据时,先去缓存REDIS找,找不到的话去数据库读取,替换不热的缓存,不热的缓存刷回数据库;能找到的话直接修改,这不存在一致性问题。然而LZ要并发访问REDIS和SQL,这样要保持一致性的话,读的时候就不能写了,就是客户端更新REDIS,然后REDIS回写数据库,这是一个事务,如果有一步不成功,那么整个事务不成功;如果是多客户端,如果数据木有分块,那么所有写操作串行话,如果对数据进行了合理分块,同一块数据数据串行写,不同块并发处理。如果保证读的时候不能写,这就需要一个主控节点来提供分布式锁。

      我们热数据基本都是redis,增删改都是操作mysql,对于读是保存到redis,这样就涉及到数据同步操作,同步操作分为两大块,我们的叫法是,一个是全量(将全部数据一次写入到redis,时间几小时不等),一个是增量(实时更新)。这里说的是增量,主要问题是即时性,因为增删改都是直接操作mysql变更都在MySQL(这里高并发的问题是用分库分表加外层的负载均衡) 所以我们的方向是读取binlog然后分析 ,利用消息推送到某服务器A,再进行分析,然后更新各台redis消息推送工具用的是rabbitMQ,可设定某表的变更推送(分三类update insert delate 包含变更前后的数据),这里有个问题是:mysql数据操作太频繁产生的推送可能会很多,所以分析处理脚本处理速度一定要跟得上(我用Python写,前期多线程(坑),后来改成多进程),还有一个问题是,对于mysql-redis的数据关系映射设定不要太复杂,一表对一表就行,数据组合交给业务层做,这样分析处理脚本不会太多负担,处理速度更快,而且操作redis也更简单,redis每个对应mysql数据表的可使用多端口多实例,redis是单线程而且这样对于redis的主从和负载均衡有利,题外话:对于服务器A 可以再给其它服务做一个数据表增量变更数据获取接口,利用数据纬度,获取时间段的变更数据。追加,对于订单类部分,都是完全使用mysql,这个做好数据服务器,DB,table,分区,的拆分就好了,看并发请求越多拆分越多。
     
3、解释MySQL索引、b树,为啥不用平衡二叉树或红黑树(磁盘与内存的存储方式不同) 
4、Spring的bean加载机制,bean生成的具体步骤 
5、IOC的注入方式(set注入、构造方法注入) 
6、Spring何时创建applicationContext(web.xml中使用listener) 
7、listener是监听哪个事件 
8、SpringMVC的具体流程 
9、Spring的特性,SpringMVC中控制器的名字及其配置 
10、Spring用到哪些设计模式? 
11、controller怎么处理的请求

四、关于数据结构与算法 
1、01矩阵,相邻的1是一个岛屿,找出所有岛屿数(递归解决) 
2、2个鸡蛋,100高的楼,最少的次数测出鸡蛋碎掉的临界层,没碎的鸡蛋可以反复使用 
3、大量字符串找出数量最多的K个,考虑内存放得下和放不下两种情况 
4、跳表的基本原理 
5、最长回文子串 
6、找到数组中最多的那个数,除了hashset还有没有其他方法(蒙特卡罗法) 
7、输入合法的数字字符串,输出货币字符(就是每隔三位加逗号,考虑边界处理) 
8、有1-10 10个数,和一个数sum,计算用10个数连加得到sum有多少种系数组合(递归) 
9、如何实现高效的同步链表 
10、层次遍历二叉树,偶数层输出,且逆序输出 
11、给定一个字符串,写出所有可能的全排列(递归) 
12、5亿条淘宝交易订单,输出销售次数最多的100个商品。 
13、给出年月日,如何求出这天是这年的第几天? 
14、64个马有8个跑道,没有计时工具,最少需要多少次跑,可以找到跑的最快的那个马,如果换成找到最快的四匹呢? 
15、给一个函数,返回0和1,概率为p和1-p,请你实现一个函数,使得返回0和1的概率一样 
16、10亿个url,每个url大小小于56B,要求去重,内存4G 
17、吧一个BST转化成一个双向链表 
18、手写一个全排列 
19、B数和B+数 
20、介绍一下hash,怎么解决hash冲突 
21、给一个文件,找出其中每个单词的个数 
22、1000瓶药有1瓶有毒,如果小白鼠服用有毒的药,则24小时后死亡。现在需设计一种策略,使用尽可能少的小白鼠,在24小时内找出有毒的药。(将串行的二分法并行使用)

五、关于Http协议 
1、TCP和UDP的区别,分别适用于哪些场景? 
2、三次握手四次握手,是否可以两次握手,为何要四次握手? 

      先来解释下题目中描述的问题“TCP建立握手时为什么是三次握手而不是二次握手或者四次握手”,结论当然是二次握手和四次握手也都是可以的,那么为什么是三次呢?这是个经典的“两军通信”问题,假设场景是这样的:在山的两头是红军1和红军2,山上是蓝军,红军1和2都不是蓝军的对手,想打败蓝军只有一起动手。那么红军1和2如何通信才能确定双方在同一个时间动手呢?红军1的传令军成功偷偷越过大山告诉红军2,明天早上中午十二点一起动手;红军2表示赞同,但是红军1并不确定红军2是否收到了消息,贸然动手必定失败,所以红军2的传令军越过大山告诉红军1收到了消息;但是这时候红军2并不确定传令军是否成功到达红军1告诉红军1收到了消息,不敢独自动手,所以红军1只好再次派出传令军,告诉红军2我们知道你们收到消息了;问题又来了,红军1不知道传令是否成功,只有等待红军2的确认并不敢动手,红军2也只能再次派出传令军。。。最后陷入了一个死循环,那么根本问题就暴露了:永远都不能完美的达成协议,而这正是一个一个重要的通信道理:世界上不存在完全可靠的通信协议。

       从通信时间成本空间成本以及可靠度来讲,选择了“三次握手”作为点对点通信的一般规则。关于建立连接时的三次握手大家可能还有两个问题:三次握手真的可靠吗?当然不可靠,任意次的握手都是不可靠的,握手成功只能说明握手时的通信是正常的,并不能保证握手后的通信是正常的,例如不稳定的网络环境,这些都是可能的。握手只能保证尽可能的可靠,而不可能保证理论上的绝对可靠。通信一定要三次握手吗?当然不是,比如寻人启示,没有做三次握手,但你能说寻人启示就不能完成通信功能吗?非三次握手建立的通信未必不可靠,三次握手建立的通信也未必可靠。
3、get和post的区别 

     1. get是从服务器上获取数据,post是向服务器传送数据。
     2. get是把参数数据队列加到提交表单的ACTION属性所指的URL中,值和表单内各个字段一一对应,在URL中可以看到。post是通过HTTP post机制,将表单内各个字段与其内容放置在HTML HEADER内一起传送到ACTION属性所指的URL地址。用户看不到这个过程。
      3. 对于get方式,服务器端用Request.QueryString获取变量的值,对于post方式,服务器端用Request.Form获取提交的数据。
      4. get传送的数据量较小,不能大于2KB。post传送的数据量较大,一般被默认为不受限制。但理论上,IIS4中最大量为80KB,IIS5中为100KB。
       5. get安全性非常低,post安全性较高。但是执行效率却比Post方法好。 
     建议:
           1 、get方式的安全性较Post方式要差些,包含机密信息的话,建议用Post数据提交方式;
           2、在做数据查询时,建议用Get方式;而在做数据添加、修改或删除时,建议用Post方式;
4、Http协议相关

     HTTP是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。它于1990年提出,经过几年的使用与发展,得到不断地完善和扩展。目前在WWW中使用的是HTTP/1.0的第六版,HTTP/1.1的规范化工作正在进行之中,而且HTTP-NG(Next Generation of HTTP)的建议已经提出。
     HTTP协议的主要特点可概括如下:
           1.支持客户/服务器模式。
           2.简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
           3.灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
           4.无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
           5.无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。

      http(超文本传输协议)是一个基于请求与响应模式的、无状态的、应用层的协议,常基于TCP的连接方式,HTTP1.1版本中给出一种持续连接的机制,绝大多数的Web开发,都是构建在HTTP协议之上的Web应用。

       http请求由三部分组成,分别是:请求行、消息报头、请求正文

       在接收和解释请求消息后,服务器返回一个HTTP响应消息。
       HTTP响应也是由三个部分组成,分别是:状态行、消息报头、响应正文

       HTTP消息由客户端到服务器的请求和服务器到客户端的响应组成。请求消息和响应消息都是由开始行(对于请求消息,开始行就是请求行,对于响应消息,开始行就是状态行),消息报头(可选),空行(只有CRLF的行),消息正文(可选)组成。
       HTTP消息报头包括普通报头、请求报头、响应报头、实体报头。每一个报头域都是由名字+“:”+空格+值 组成,消息报头域的名字是大小写无关的。

六、关于Linux 
1、epoll、epollh和select的区别 

select、poll、epoll简介

 

epoll跟select都能提供多路I/O复用的解决方案。在现在的Linux内核里有都能够支持,其中epoll是Linux所特有,而select则应该是POSIX所规定,一般操作系统均有实现

select:

select本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理。这样所带来的缺点是:

1、 单个进程可监视的fd数量被限制,即能监听端口的大小有限。

      一般来说这个数目和系统内存关系很大,具体数目可以cat /proc/sys/fs/file-max察看。32位机默认是1024个。64位机默认是2048.

2、 对socket进行扫描时是线性扫描,即采用轮询的方法,效率较低:

       当套接字比较多的时候,每次select()都要通过遍历FD_SETSIZE个Socket来完成调度,不管哪个Socket是活跃的,都遍历一遍。这会浪费很多CPU时间。如果能给套接字注册某个回调函数,当他们活跃时,自动完成相关操作,那就避免了轮询,这正是epoll与kqueue做的。

3、需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大

poll:

poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程经历了多次无谓的遍历。

它没有最大连接数的限制,原因是它是基于链表来存储的,但是同样有一个缺点:

1、大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。                   

2、poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。

epoll:

epoll有EPOLLLT和EPOLLET两种触发模式,LT是默认的模式,ET是“高速”模式。LT模式下,只要这个fd还有数据可读,每次 epoll_wait都会返回它的事件,提醒用户程序去操作,而在ET(边缘触发)模式中,它只会提示一次,直到下次再有数据流入之前都不会再提示了,无 论fd中是否还有数据可读。所以在ET模式下,read一个fd的时候一定要把它的buffer读光,也就是说一直读到read的返回值小于请求值,或者 遇到EAGAIN错误。还有一个特点是,epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知。

epoll为什么要有EPOLLET触发模式?

如果采用EPOLLLT模式的话,系统中一旦有大量你不需要读写的就绪文件描述符,它们每次调用epoll_wait都会返回,这样会大大降低处理程序检索自己关心的就绪文件描述符的效率.。而采用EPOLLET这种边沿触发模式的话,当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据全部读写完(如读写缓冲区太小),那么下次调用epoll_wait()时,它不会通知你,也就是它只会通知你一次,直到该文件描述符上出现第二次可读写事件才会通知你!!!这种模式比水平触发效率高,系统不会充斥大量你不关心的就绪文件描述符

epoll的优点:

1、没有最大并发连接的限制,能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口);
2、效率提升,不是轮询的方式,不会随着FD数目的增加效率下降。只有活跃可用的FD才会调用callback函数;
即Epoll最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,Epoll的效率就会远远高于select和poll。

3、 内存拷贝,利用mmap()文件映射内存加速与内核空间的消息传递;即epoll使用mmap减少复制开销。
select、poll、epoll 区别总结:

1、支持一个进程所能打开的最大连接数

select

单个进程所能打开的最大连接数有FD_SETSIZE宏定义,其大小是32个整数的大小(在32位的机器上,大小就是3232,同理64位机器上FD_SETSIZE为3264),当然我们可以对进行修改,然后重新编译内核,但是性能可能会受到影响,这需要进一步的测试。

poll

poll本质上和select没有区别,但是它没有最大连接数的限制,原因是它是基于链表来存储的

epoll

虽然连接数有上限,但是很大,1G内存的机器上可以打开10万左右的连接,2G内存的机器可以打开20万左右的连接

2、FD剧增后带来的IO效率问题

select

因为每次调用时都会对连接进行线性遍历,所以随着FD的增加会造成遍历速度慢的“线性下降性能问题”。

poll

同上

epoll

因为epoll内核中实现是根据每个fd上的callback函数来实现的,只有活跃的socket才会主动调用callback,所以在活跃socket较少的情况下,使用epoll没有前面两者的线性下降的性能问题,但是所有socket都很活跃的情况下,可能会有性能问题。

3、 消息传递方式

select

内核需要将消息传递到用户空间,都需要内核拷贝动作

poll

同上

epoll

epoll通过内核和用户空间共享一块内存来实现的。

总结:

综上,在选择select,poll,epoll时要根据具体的使用场合以及这三种方式的自身特点。

1、表面上看epoll的性能最好,但是在连接数少并且连接都十分活跃的情况下,select和poll的性能可能比epoll好,毕竟epoll的通知机制需要很多函数回调。

2、select低效是因为每次它都需要轮询。但低效也是相对的,视情况而定,也可通过良好的设计改


2、Linux下的一些指令,怎么查看进程,按照内存大小,CPU占用排序等

     Linux TOP命令 按内存占用排序和按CPU占用排序
          P – 以 CPU 占用率大小的顺序排列进程列表
          M – 以内存占用率大小的顺序排列进程列

    命令:chmod (change mode) 
    功能:改变文件的读写和执行权限。有符号法和八进制数字法

    修改文件日期 
    命令:touch 
    格式:touch filenae 
    功能:改变文件的日期,不对文件的内容做改动,若文件不存在则建立新文件。 
    例如:% touch file 


    链接文件 
    命令:ln (link) 
    格式:ln [option] filename linkname 
          ln [option] directory pathname 
    功能:为文件或目录建立一个链。其中,filename和directory是源文件名和 
          源目录名;linkname和pathname分别表示与源文件或源目录名相链接的 
          文件或目录。 
    选项:-s  为文件或目录建立符号链接。不加-s表示为文件或目录建立硬链接 
    注释:链接的目地在于,对一个文件或目录赋予两个以上的名字,使其可以出 
          现在不同的目录中,既可以使文件或目录共享,又可以节省磁盘空间。 
    例如:% ln -s filename linkname 


    显示日期 
    命令:date 
    例如:% date 


    显示日历 
    命令:cal (calendar) 
    格式:cal [month] year 
    功能:显示某年内指定的日历 
    例如:% cal 1998  


    显示文件头部 
    命令:head 
    格式:head [option] filename 
    功能:显示文件的头部 
    选项:缺省  显示文件的头10行。 
          -i    显示文件的开始 i行。 
    例如:% head filename 


     显示文件尾部 
    命令:tail 
    格式:tail [option] filename 
    功能:显示文件的尾部 
    选项:缺省  显示文件的末10行。 
          -i    显示文件最后 i行。 
          +i    从文件的第i行开始显示。 
    例如:% tail filename 


    显示用户标识 
    命令:id 
    格式:id [option] [user] 
    功能:显示用户标识及用户所属的所有组。 
    选项:-a 显示用户名、用户标识及用户所属的所有组 
    注释: 
    例如:% id username 


    查看当前登录的用户 
    命令:users 


    显示都谁登录到机器上 
    命令:who 
    格式:who 
    功能:显示当前正在系统中的所有用户名字,使用终端设备号,注册时间。 
    例如:% who 


    显示当前终端上的用户名 
    命令:whoami 
    格式:whoami 
    功能:显示出当前终端上使用的用户。 
    例如:% whoami 

    显示磁盘空间 
    命令:df (disk free) 
    格式:df [option] 
    功能:显示磁盘空间的使用情况,包括文件系统安装的目录名、块设备名、总 
          字节数、已用字节数、剩余字节数占用百分比。 
    选项: 
-a:显示全部的档案系统和各分割区的磁盘使用情形 
-i:显示i -nodes的使用量 
-k:大小用k来表示 (默认值) 
-t:显示某一个档案系统的所有分割区磁盘使用量 
-x:显示不是某一个档案系统的所有分割区磁盘使用量 
-T:显示每个分割区所属的档案系统名称 
-h: 表示使用「Human-readable」的输出,也就是在档案系统大小使用 GB、MB 等易读的格式。 
    注释: 
    例如:% df -hi 

    查询档案或目录的磁盘使用空间 
    命令:du (disk usage) 
    格式:du [option] [filename] 
    功能:以指定的目录下的子目录为单位,显示每个目录内所有档案所占用的磁盘空间大小 
    选项: 
-a:显示全部目录和其次目录下的每个档案所占的磁盘空间 
-b:大小用bytes来表示 (默认值为k bytes) 
-c:最后再加上总计 (默认值) 
-s:只显示各档案大小的总合 
-x:只计算同属同一个档案系统的档案 
-L:计算所有的档案大小 
-h: 表示档案系统大小使用 GB、MB 等易读的格式。 
    例如:% du -a 
% du -sh /etc 只显示该目录的总合 
% du /etc | sort -nr | more 统计结果用sort 指令进行排序, 
sort 的参数 -nr 表示要以数字排序法进行反向排序。 


    显示进程 
    命令:ps 
    格式:ps [option] 
    功能:显示系统中进程的信息。包括进程ID、控制进程终端、执行时间和命令。 
    选项: 
  -a 显示所有进程信息 
  -U uidlist 列出这个用户的所有进程 
          -e 显示当前运行的每一个进程信息 
          -f 显示一个完整的列表 
  -x 显示包括没有终端控制的进程状况 。 
    注释: 
    例如:% ps -ef 
  % ps -aux 然后再利用一个管道符号导向到grep去查找特定的进程,然后再对特定的进程进行操作。 

    终止进程 
    命令:kill 
    格式:kill [option] pid 
    功能:向指定的进程送信号或终止进程。kill指令的用途是送一个signal给某一个process, 
    因为大部份送的都是用来杀掉 process 的 SIGKILL 或 SIGHUP ,因此称为 kill  
    选项:-9  强行终止进程 
    注释:pid标示进程号,可由ps命令得到。 
    例如:% kill -9 pid 
    你也可以用 kill -l 来察看可代替 signal 号码的数目字。kill 的详细情形请参阅 man kill。 


    查看自己的IP地址 
    命令:ifconfig 
    格式:ifconfig -a 
   
    查看路由表 
    命令:netstat 
    格式:netstat -rn 


    远程登录 
    命令:telnet 
    格式:telnet hostname 


    文件传输 
    命令:ftp (file transfer program) 
    格式:ftp hostname 
    功能:网络文件传输及远程操作。 
    

七、关于数据库 
1、数据库四个范式的区别,在设计数据库的时候怎么考量,详细设计数据库的步骤。 
2、MySQL和Oracle的区别,如何选择? 
3、SQL中replace和update的区别 

  这两个命令都是数据修改命令。
    update是SQL中的数据更新命令,replace是VF中的数据更新命令;
    update不需要打开表,replace命令需要先打开表;
    update不加条件是更新表中所有记录,replace不加条件是更新当前记录,要加上all短语才是更新所有记录
4、事务管理acid 
5、数据库的索引有什么用,带来的问题是什么? 

为什么要创建索引呢?这是因为,创建索引可以大大提高系统的性能。 
第一,通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。 
第二,可以大大加快 数据的检索速度,这也是创建索引的最主要的原因。 
第三,可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。 
第四,在使用分组和排序 子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。 
第五,通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。 


也许会有人要问:增加索引有如此多的优点,为什么不对表中的每一个列创建一个索引呢?这种想法固然有其合理性,然而也有其片面性。虽然,索引有许多优点,但是,为表中的每一个列都增加索引,是非常不明智的。这是因为,增加索引也有许多不利的一个方面。 

第一,创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。 
第二,索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大。 
第三,当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。 


索引是建立在数据库表中的某些列的上面。因此,在创建索引的时候,应该仔细考虑在哪些列上可以创建索引,在哪些列上不能创建索引。一般来说,应该在这些列上创建索引,例如: 


在经常需要搜索的列上,可以加快搜索的速度; 
在作为主键的列上,强制该列的唯一性和组织表中数据的排列结构; 
在经常用在连接的列上,这 些列主要是一些外键,可以加快连接的速度; 
在经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的; 
在经常需要排序的列上创 建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间; 
在经常使用在WHERE子句中的列上面创建索引,加快条件的判断速度。 

同样,对于有些列不应该创建索引。一般来说,不应该创建索引的的这些列具有下列特点: 
第一,对于那些在查询中很少使用或者参考的列不应该创建索引。这是因 为,既然这些列很少使用到,因此有索引或者无索引,并不能提高查询速度。相反,由于增加了索引,反而降低了系统的维护速度和增大了空间需求。 
第二,对于那 些只有很少数据值的列也不应该增加索引。这是因为,由于这些列的取值很少,例如人事表的性别列,在查询的结果中,结果集的数据行占了表中数据行的很大比例,即需要在表中搜索的数据行的比例很大。增加索引,并不能明显加快检索速度。 
第三,对于那些定义为text, image和bit数据类型的列不应该增加索引。这是因为,这些列的数据量要么相当大,要么取值很少。 
第四,当修改性能远远大于检索性能时,不应该创建索 引。这是因为,修改性能和检索性能是互相矛盾的。当增加索引时,会提高检索性能,但是会降低修改性能。当减少索引时,会提高修改性能,降低检索性能。因 此,当修改性能远远大于检索性能时,不应该创建索引。 


创建索引的方法和索引的特征 
创建索引的方法 
创 建索引有多种方法,这些方法包括直接创建索引的方法和间接创建索引的方法。直接创建索引,例如使用CREATE INDEX语句或者使用创建索引向导,间接创建索引,例如在表中定义主键约束或者唯一性键约束时,同时也创建了索引。虽然,这两种方法都可以创建索引,但 是,它们创建索引的具体内容是有区别的。 
直接创建索引:
使用CREATE INDEX语句或者使用创建索引向导来创建索引,这是最基本的索引创建方式,并且这种方法最具有柔性,可以定制创建出符合自己需要的索引。在使用这种方式 创建索引时,可以使用许多选项,例如指定数据页的充满度、进行排序、整理统计信息等,这样可以优化索引。使用这种方法,可以指定索引的类型、唯一性和复合 性,也就是说,既可以创建聚簇索引,也可以创建非聚簇索引,既可以在一个列上创建索引,也可以在两个或者两个以上的列上创建索引。 
间接创建索引:
通 过定义主键约束或者唯一性键约束,也可以间接创建索引。主键约束是一种保持数据完整性的逻辑,它限制表中的记录有相同的主键记录。在创建主键约束时,系统自动创建了一个唯一性的聚簇索引。虽然,在逻辑上,主键约束是一种重要的结构,但是,在物理结构上,与主键约束相对应的结构是唯一性的聚簇索引。换句话说,在物理实现上,不存在主键约束,而只存在唯一性的聚簇索引。同样,在创建唯一性键约束时,也同时创建了索引,这种索引则是唯一性的非聚簇索引。因此,当使用约束创建索引时,索引的类型和特征基本上都已经确定了,由用户定制的余地比较小。 


当在表上定义主键或者唯一性键约束时,如果表 中已经有了使用CREATE INDEX语句创建的标准索引时,那么主键约束或者唯一性键约束创建的索引覆盖以前创建的标准索引。也就是说,主键约束或者唯一性键约束创建的索引的优先 级高于使用CREATE INDEX语句创建的索引。 


索引的特征 
索引有两个特征,即唯一性索引和复合索引。 
唯一 性索引保证在索引列中的全部数据是唯一的,不会包含冗余数据。如果表中已经有一个主键约束或者唯一性键约束,那么当创建表或者修改表时,SQL Server自动创建一个唯一性索引。然而,如果必须保证唯一性,那么应该创建主键约束或者唯一性键约束,而不是创建一个唯一性索引。当创建唯一性索引 时,应该认真考虑这些规则:当在表中创建主键约束或者唯一性键约束时,SQL Server自动创建一个唯一性索引;如果表中已经包含有数据,那么当创建索引时,SQL Server检查表中已有数据的冗余性;每当使用插入语句插入数据或者使用修改语句修改数据时,SQL Server检查数据的冗余性:如果有冗余值,那么SQL Server取消该语句的执行,并且返回一个错误消息;确保表中的每一行数据都有一个唯一值,这样可以确保每一个实体都可以唯一确认;只能在可以保证实体 完整性的列上创建唯一性索引,例如,不能在人事表中的姓名列上创建唯一性索引,因为人们可以有相同的姓名。 


复合索引就是一个索引创建 在两个列或者多个列上。在搜索时,当两个或者多个列作为一个关键值时,最好在这些列上创建复合索引。当创建复合索引时,应该考虑这些规则:最多可以把16个列合并成一个单独的复合索引,构成复合索引的列的总长度不能超过900字节,也就是说复合列的长度不能太长;在复合索引中,所 有的列必须来自同一个表中,不能跨表建立复合列;在复合索引中,列的排列顺序是非常重要的,因此要认真排列列的顺序,原则上,应该首先定义最唯一的列,例 如在(COL1,COL2)上的索引与在(COL2,COL1)上的索引是不相同的,因为两个索引的列的顺序不同;为了使查询优化器使用复合索引,查询语 句中的WHERE子句必须参考复合索引中第一个列;当表中有多个关键列时,复合索引是非常有用的;使用复合索引可以提高查询性能,减少在一个表中所创建的 索引数量。
 
 
索引的类型 
根据索引的顺序与数据表的物理顺序是否相同,可以把索引分成两种类型。一种是数据表的物理顺序与索引顺序相同的聚簇索引,另一种是数据表的物理顺序与索引顺序不相同的非聚簇索引。 


聚簇索引的体系结构 
索 引的结构类似于树状结构,树的顶部称为叶级,树的其它部分称为非叶级,树的根部在非叶级中。同样,在聚簇索引中,聚簇索引的叶级和非叶级构成了一个树状结构,索引的最低级是叶级。在聚簇索引中,表中的数据所在的数据页是叶级,在叶级之上的索引页是非叶级,索引数据所在的索引页是非叶级。在聚簇索引中,数据值的顺序总是按照升序排列。 


应该在表中经常搜索的列或者按照顺序访问的列上创建聚簇索引。当创建聚簇索引时,应该考虑这些因素:每一个表只能有一个聚簇索引,因为表中数据的物理顺序 只能有一个;表中行的物理顺序和索引中行的物理顺序是相同的,在创建任何非聚簇索引之前创建聚簇索引,这是因为聚簇索引改变了表中行的物理顺序,数据行按 照一定的顺序排列,并且自动维护这个顺序;关键值的唯一性要么使用UNIQUE关键字明确维护,要么由一个内部的唯一标识符明确维护,这些唯一性标识符是 系统自己使用的,用户不能访问;聚簇索引的平均大小大约是数据表的百分之五,但是,实际的聚簇索引的大小常常根据索引列的大小变化而变化;在索引的创建过 程中,SQL Server临时使用当前数据库的磁盘空间,当创建聚簇索引时,需要1.2倍的表空间的大小,因此,一定要保证有足够的空间来创建聚簇索引。 


当 系统访问表中的数据时,首先确定在相应的列上是否存在有索引和该索引是否对要检索的数据有意义。如果索引存在并且该索引非常有意义,那么系统使用该索引访问表中的记录。系统从索引开始浏览到数据,索引浏览则从树状索引的根部开始。从根部开始,搜索值与每一个关键值相比较,确定搜索值是否大于或者等于关键值。这一步重复进行,直到碰上一个比搜索值大的关键值,或者该搜索值大于或者等于索引页上所有的关键值为止。 


非聚簇索引的体系结构 
非聚簇索引的结构也是树状结构,与聚簇索引的结构非常类似,但是也有明显的不同。 
在非聚簇索引中,叶级仅包含关键值,而没有包含数据行。非聚簇索引表示行的逻辑顺序。 非聚簇索引有两种体系结构:一种体系结构是在没有聚簇索引的表上创建非聚簇索引,另一种体系结构是在有聚簇索引的表上创建非聚簇索引。 


如 果一个数据表中没有聚簇索引,那么这个数据表也称为数据堆。当非聚簇索引在数据堆的顶部创建时,系统使用索引页中的行标识符指向数据页中的记录。行标识符存储了数据所在位置的信息。数据堆是通过使用索引分配图(IAM)页来维护的。IAM页包含了数据堆所在簇的存储信息。在系统表sysindexes中,有一个指针指向了与数据堆相关的第一个IAM页。系统使用IAM页在数据堆中浏览和寻找可以插入新的记录行的空间。这些数据页和在这些数据页中的记录 没有任何的顺序并且也没有链接在一起。在这些数据页之间的唯一的连接是IAM中记录的顺序。当在数据堆上创建了非聚簇索引时,叶级中包含了指向数据页的行 标识符。行标识符指定记录行的逻辑顺序,由文件ID、页号和行ID组成。这些行的标识符维持唯一性。非聚簇索引的叶级页的顺序不同于表中数据的物理顺序。 这些关键值在叶级中以升序维持。 


当非聚簇索引创建在有聚簇索引的表上的时候,系统使用索引页中的指向聚簇索引的聚簇键。聚簇键存储了数据的位置信息。如果某一个表有聚簇索引,那么非聚簇 索引的叶级包含了映射到聚簇键的聚簇键值,而不是映射到物理的行标识符。当系统访问有非聚簇索引的表中数据时,并且这种非聚簇索引创建在聚簇索引上,那么 它首先从非聚簇索引来找到指向聚簇索引的指针,然后通过使用聚簇索引来找到数据。 
当需要以多种方式检索数据时,非聚簇索引是非常有用的。当创建非聚簇索引时,要考虑这些情况:在缺省情况下,所创建的索引是非聚簇索引;在每一个表上面,可以创建不多于249个非聚簇索引,而聚簇索引最多只能有一个。 
系统如何访问表中的数据 
一 般地,系统访问数据库中的数据,可以使用两种方法:表扫描和索引查找。第一种方法是表扫描,就是指系统将指针放置在该表的表头数据所在的数据页上,然后按照数据页的排列顺序,一页一页地从前向后扫描该表数据所占有的全部数据页,直至扫描完表中的全部记录。在扫描时,如果找到符合查询条件的记录,那么就将这条记录挑选出来。最后,将全部挑选出来符合查询语句条件的记录显示出来。第二种方法是使用索引查找。索引是一种树状结构,其中存储了关键字和指向包含关键字所在记录的数据页的指针。当使用索引查找时,系统沿着索引的树状结构,根据索引中关键字和指针,找到符合查询条件的的记录。最后,将全部查找到的符合查询语句条件的记录显示出来。 
在SQL Server中,当访问数据库中的数据时,由SQL Server确定该表中是否有索引存在。如果没有索引,那么SQL Server使用表扫描的方法访问数据库中的数据。查询处理器根据分布的统计信息生成该查询语句的优化执行规划,以提高访问数据的效率为目标,确定是使用 表扫描还是使用索引。 
索引的选项 
在创建索引时,可以指定一些选项,通过使用这些选项,可以优化索引的性能。这些选项包括FILLFACTOR选项、PAD_INDEX选项和SORTED_DATA_REORG选项。 
使 用FILLFACTOR选项,可以优化插入语句和修改语句的性能。当某个索引页变满时,SQL Server必须花费时间分解该页,以便为新的记录行腾出空间。使用FILLFACTOR选项,就是在叶级索引页上分配一定百分比的自由空间,以便减少页 的分解时间。当在有数据的表中创建索引时,可以使用FILLFACTOR选项指定每一个叶级索引节点的填充的百分比。缺省值是0,该数值等价于100。在 创建索引的时候,内部索引节点总是留有了一定的空间,这个空间足够容纳一个或者两个表中的记录。在没有数据的表中,当创建索引的时候,不要使用该选项,因 为这时该选项是没有实际意义的。另外,该选项的数值在创建时指定以后,不能动态地得到维护,因此,只应该在有数据的表中创建索引时才使用。 
PAD_INDEX 选项将FILLFACTOR选项的数值同样也用于内部的索引节点,使内部的索引节点的填充度与叶级索引的节点中的填充度相同。如果没有 指定FILLFACTOR选项,那么单独指定PAD_INDEX选项是没有实际意义的,这是因为PAD_INDEX选项的取值是由FILLFACTOR选 项的取值确定的。 
当创建聚簇索引时,SORTED_DATA_REORG选项清除排序,因此可以减少建立聚簇索引所需要的时间。当在一个已经 变成碎块的表上创建或者重建聚 簇索引时,使用SORTED_DATA_REORG选项可以压缩数据页。当重新需要在索引上应用填充度时,也使用该选项。当使用SORTED_DATA_REORG选项时,应该考虑这些因素:SQL Server确认每一个关键值是否比前一个关键值高,如果都不高,那么不能创建索引;SQL Server要求1.2倍的表空间来物理地重新组织数据;使用SORTED_DATA_REORG选项,通过清除排序进程而加快索引创建进程;从表中物理 地拷贝数据;当某一个行被删除时,其所占的空间可以重新利用;创建全部非聚簇索引;如果希望把叶级页填充到一定的百分比,可以同时使用 FILLFACTOR选项和SORTED_DATA_REORG选项。 
索引的维护 
为了维护系统性能,索引在创建之后,由于频繁地对数据进行增加、删除、修改等操作使得索引页发生碎块,因此,必须对索引进行维护。 
使 用DBCC SHOWCONTIG语句,可以显示表的数据和索引的碎块信息。当执行DBCC SHOWCONTIG语句时,SQL Server浏览叶级上的整个索引页,来确定表或者指定的索引是否严重碎块。DBCC SHOWCONTIG语句还能确定数据页和索引页是否已经满了。当对表进行大量的修改或者增加大量的数据之后,或者表的查询非常慢时,应该在这些表上执行DBCC SHOWCONTIG语句。当执行DBCC SHOWCONTIG语句时,应该考虑这些因素:当执行DBCC SHOWCONTIG语句时,SQL Server要求指定表的ID号或者索引的ID号,表的ID号或者索引的ID号可以从系统表sysindexes中得到;应该确定多长时间使用一次DBCC SHOWCONTIG语句,这个时间长度要根据表的活动情况来定,每天、每周或者每月都可以。 
使用DBCC DBREINDEX语句重建表的一个或者多个索引。当希望重建索引和当表上有主键约束或者唯一性键约束时,执行DBCC DBREINDEX语句。除此之外,执行DBCC DBREINDEX语句还可以重新组织叶级索引页的存储空间、删除碎块和重新计算索引统计。当使用执行DBCC DBREINDEX语句时,应该考虑这些因素:根据指定的填充度,系统重新填充每一个叶级页;使用DBCC DBREINDEX语句重建主键约束或者唯一性键约束的索引;使用SORTED_DATA_REORG选项可以更快地创建聚簇索引,如果没有排列关键值, 那么不能使用DBCC DBREINDEX语句;DBCC DBREINDEX语句不支持系统表。另外,还可以使用数据库维护规划向导自动地进行重建索引的进程。 
统计信息是存储在SQL Server中的列数据的样本。这些数据一般地用于索引列,但是还可以为非索引列创建统计。SQL Server维护某一个索引关键值的分布统计信息,并且使用这些统计信息来确定在查询进程中哪一个索引是有用的。查询的优化依赖于这些统计信息的分布准确 度。查询优化器使用这些数据样本来决定是使用表扫描还是使用索引。当表中数据发生变化时,SQL Server周期性地自动修改统计信息。索引统计被自动地修改,索引中的关键值显著变化。统计信息修改的频率由索引中的数据量和数据改变量确定。例如,如 果表中有10000行数据,1000行数据修改了,那么统计信息可能需要修改。然而,如果只有50行记录修改了,那么仍然保持当前的统计信息。除了系统自 动修改之外,用户还可以通过执行UPDATE STATISTICS语句或者sp_updatestats系统存储过程来手工修改统计信息。使用UPDATE STATISTICS语句既可以修改表中的全部索引,也可以修改指定的索引。 
使用SHOWPLAN和STATISTICS IO语句可以分析索引和查询性能。使用这些语句可以更好地调整查询和索引。SHOWPLAN语句显示在连接表中使用的查询优化器的每一步以及表明使用哪一 个索引访问数据。使用SHOWPLAN语句可以查看指定查询的查询规划。当使用SHOWPLAN语句时,应该考虑这些因素。SET SHOWPLAN_ALL语句返回的输出结果比SET SHOWPLAN_TEXT语句返回的输出结果详细。然而,应用程序必须能够处理SET SHOWPLAN_ALL语句返回的输出结果。SHOWPLAN语句生成的信息只能针对一个会话。如果重新连接SQL Server,那么必须重新执行SHOWPLAN语句。STATISTICS IO语句表明输入输出的数量,这些输入输出用来返回结果集和显示指定查询的逻辑的和物理的I/O的信息。可以使用这些信息来确定是否应该重写查询语句或者 重新设计索引。使用STATISTICS IO语句可以查看用来处理指定查询的I/O信息。 
就象SHOWPLAN语句一样,优化器隐藏也用来 调整查询性能。优化器隐藏可以对查询性能提供较小的改进,并且如果索引策略发生了改变,那么这种优化器隐 藏就毫无用处了。因此,限制使用优化器隐藏,这是因为优化器隐藏更有效率和更有柔性。当使用优化器隐藏时,考虑这些规则:指定索引名称、当index_id为0时为使用表扫描、当index_id为1时为使用聚簇索引;优化器隐藏覆盖查询优化器,如果数据或者环境发生了变化,那么必须修改优 化器隐藏。
 

 
索引调整向导 
索引调整向导是一种工具,可以分析一系列数据库的查询语句,提供使用一系列数据库索引的建议,优化整个查询语句的性能。对于查询语句,需要指定下列内容: 
查询语句,这是将要优化的工作量 
包含了这些表的数据库,在这些表中,可以创建索引,提高查询性能 
在分析中使用的表 
在分析中,考虑的约束条件,例如索引可以使用的最大磁盘空间 
这 里指的工作量,可以来自两个方面:使用SQL Server捕捉的轨迹和包含了SQL语句的文件。索引调整向导总是基于一个已经定义好的工作量。如果一个工作量不能反映正常的操作,那么它建议使用的索 引不是实际的工作量上性能最好的索引。索引调整向导调用查询分析器,使用所有可能的组合评定在这个工作量中每一个查询语句的性能。然后,建议在整个工作量 上可以提高整个查询语句的性能的索引。如果没有供索引调整向导来分析的工作量,那么可以使用图解器立即创建它。一旦决定跟踪一条正常数据库活动的描述样 本,向导能够分析这种工作量和推荐能够提高数据库工作性能的索引配置。 
索引调整向导对工作量进行分析之后,可以查看到一系列的报告,还可以使该向导立即创建所建议的最佳索引,或者使这项工作成为一种可以调度的作业,或者生成一个包含创建这些索引的SQL语句的文件。 
索引调整向导允许为SQL Server数据库选择和创建一种理想的索引组合和统计,而不要求对数据库结构、工作量或者SQL Server内部达到专家的理解程度。总之,索引调整向导能够作到以下几个方面的工作: 
通过使用查询优化器来分析工作量中的查询任务,向有大量工作量的数据库推荐一种最佳的索引混合方式 
分析按照建议作出改变之后的效果,包括索引的用法、表间查询的分布和大量工作中查询的工作效果 
为少量查询任务推荐调整数据库的方法 
通过设定高级选项如磁盘空间约束、最大的查询语句数量和每个索引的最多列的数量等,允许定制推荐方式 
图解器 
图 解器能够实时抓取在服务器中运行的连续图片,可以选取希望监测的项目和事件,包括Transact-SQL语句和批命令、对象的用法、锁定、安全事件和 错误。图解器能够过滤这些事件,仅仅显示用户关心的问题。可以使用同一台服务器或者其他服务器重复已经记录的跟踪事件,重新执行那些已经作了记录的命令。 通过集中处理这些事件,就能够很容易监测和调试SQL Server中出现的问题。通过对特定事件的研究,监测和调试SQL Server问题变得简单多了。 
查询处理器 
查询处理器是一种可以完成许多工作的多用途的工具。在查询处理器中,可以交互式地输入和 执行各种Transact-SQL语句,并且在一个窗口中可以同时 查看Transact-SQL语句和其结果集;可以在查询处理器中同时执行多个Transact-SQL语句,也可以执行脚本文件中的部分语句;提供了一 种图形化分析查询语句执行规划的方法,可以报告由查询处理器选择的数据检索方法,并且可以根据查询规划调整查询语句的执行,提出执行可以提高性能的优化索 引建议,这种建议只是针对一条查询语句的索引建议,只能提高这一条查询语句的查询性能。 
系统为每一个索引创建一个分布页,统计信息就是指存储 在分布页上的某一个表中的一个或者多个索引的关键值的分布信息。当执行查询语句时,为了提高查询速度 和性能,系统可以使用这些分布信息来确定使用表的哪一个索引。查询处理器就是依赖于这些分布的统计信息,来生成查询语句的执行规划。执行规划的优化程度依 赖于这些分布统计信息的准确步骤的高低程度。如果这些分布的统计信息与索引的物理信息非常一致,那么查询处理器可以生成优化程度很高的执行规划。相反,如 果这些统计信息与索引的实际存储的信息相差比较大,那么查询处理器生成的执行规划的优化程度则比较低。 
查询处理器从统计信息中提取索引关键字 的分布信息,除了用户可以手工执行UPDATE STATISTICS之外,查询处理器还可以自动收集统计这些分布信息。这样,就能够充分保证查询处理器使用最新的统计信息,保证执行规划具有很高的优化 程度,减少了维护的需要。当然,使用查询处理器生成的执行规划,也有一些限制。例如,使用执行规划只能提高单个查询语句的性能,但是可能对整个系统的性能 产生正面的或者付面的影响,因此,要想提高整个系统的查询性能,应该使用索引调整向导这样的工具。 
结论 
在以前的SQL Server版本中,在一个查询语句中,一个表上最多使用一个索引。而在SQL Server 7.0中,索引操作得到了增强。SQL Server现在使用索引插入和索引联合算法来实现在一个查询语句中的可以使用多个索引。共享的行标识符用于连接同一个表上的两个索引。如果某个表中有一个聚簇索引,因此有一个聚簇键,那么该表上的全部非聚簇索引的叶节点使用该聚簇键作为行定位器,而不是使用物理记录标识符。如果表中没有聚簇索引,那么非聚簇索引继续使用物理记录标识符指向数据页。在上面的两种情况中,行定位器是非常稳定的。当聚簇索引的叶节点分开时,由于行定位器是有效的,所以非聚簇索引不需要被修改。如果表中没有聚簇索引,那么页的分开就不会发生。而在以前的版本中,非聚簇索引使用物理记录标识符如页号和行号,作为行的定位器。例如,如果聚簇索引(数据页)发生分解时,许多记录行被移动到了一个新的数据页,因此有了多个新的物理记录标识符。那么,所有的非聚簇索引都必须使用这些新的物理记录标识符进行修改,这样就需要耗费大量的时间和资源。 
索引调整向导无论对熟练用户还是新用户,都是一个很好的工具。熟练用户可以使用该向导创建一个基本的索引配置,然后在基本的索引配置上面进行调整和定制。新用户可以使用该向导快速地创建优化的索引。
6、数据库查询时间优化 

   1、对查询进行优化,应尽可能避免全表扫描

   2、写数据语句时尽可能减少表的全局扫描
      2.1 减少where 字段值null判断
      2.2 应尽量避免在 where 子句中使用!=或<>操作符
      2.3 应尽量避免在 where 子句中使用 or 来连接条件
      2.4 in 和 not in 也要慎用
      2.5 少使用模糊匹配 like
      2.6 应尽量避免在 where 子句中对字段进行表达式操作
      2.7 任何地方都不要使用*通配符去查询所有

  3、不要在条件判断时进行 算数运算

   4、很多时候用 exists 代替 in 是一个好的选择

   5 论索引技巧
     5.1 并不是所有索引对查询都有效
     SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用
    5.2 索引并不是越多越好

       索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。
    5.3 应尽可能的避免更新 clustered 索引数据列
       因为 clustered 索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新 clustered 索引数据列,那么需要考虑是否应将该索引建为 clustered 索引。
    5.4 尽量使用数字型字段
       若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
   6 创建数据库时应该注意地方
    6.1. 尽可能的使用 varchar/nvarchar 代替 char/nchar
      因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
    6.2 用表变量来代替临时表。
     1. 如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。
     2. 在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。
     3. 如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。
    7 避免频繁创建和删除临时表,以减少系统表资源的消耗。
    8  尽量避免使用游标
     1. 因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。
     2. 使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。
     3. 与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。


6 数据放回时注意什么
6.1 尽量避免大事务操作,提高系统并发能力。
这样可以有效提高系统的并发能力
6.2 尽量避免向客户端返回大数据量
若数据量过大,应该考虑相应需求是否合理。

八、设计模式 
1、单例模式 
2、生产者模式、消费者模式

   

生产者与消费者模式概述

生产者与消费者模式是通过一个容器来解决生产者与消费者的强耦合关系,生产者与消费者之间不直接进行通讯,而是利用阻塞队列来进行通讯,生产者生成数据后直接丢给阻塞队列,消费者需要数据则从阻塞队列获取,实际应用中,生产者与消费者模式则主要解决生产者与消费者生产与消费的速率不一致的问题,达到平衡生产者与消费者的处理能力,而阻塞队列则相当于缓冲区。

这就好比去学校食堂吃饭,食堂大妈并不会等你点了餐之后才立马去给你做,而是先把饭菜做好了放在食堂窗口等你来自己取,而这个窗口就相当于阻塞队列,食堂大妈就是生产者,而学生就是消费者。

整个生产者与消费者模型大致如下图所示。

这里写图片描述

当然,实际情况可能需要在消费者处理完之后又作为生产者把数据放进新的队列由其他消费者处理。