阿里一面面经(Java后台开发实习生)

1. 如果有几亿的数据在一张表中,需要操作的时候应该怎么处理

2.排序算法都有哪些,快速排序的思想

3.hashmap了解吗,什么时候初始化

4.java的并发包有什么了解吗

5.原子类有了解吗,原理是什么

6.CAS的好处和坏处是什么

7.jvm的内存模型,详细说一下jvm的堆空间划分

8.jvm的gc算法,为什么要gc?full gc和minor gc的区别?分别都是什么时候触发

答案如下:

1. 如果有几亿的数据在一张表中,需要操作的时候应该怎么处理

建立索引[基本不能解决问题,数据量已经很大了]

使用redis缓存,数据库主从分离

对于数据库表中的数据进行分库,分表,分区

分表可以将一张表进行水平拆分和垂直拆分,垂直拆分是根据业务进行拆分,将一张表根据业务进行拆分,水平拆分是把多条数据拆分,将一张表分为N页,并通过一个新页(总表)记录每个页的位置

分区是指把不常用的数据进行迁移到其他地方,这个需要定期维护

2.排序算法都有哪些,快速排序的思想

选择排序,插入排序,冒泡排序,快速排序,希尔排序,基数排序,归并排序,堆排序

快排的思想是分治

确定一个基准值,通过一趟排序将要排序的数据分割成独立的两部分,左边的数据比指定的值小,右边的数据比指定的值大,然后再对左右两边分别进行递归

快排代码:

 

#include

void print(int a[],int n)

{

    int i;

    for(i=0;i

    {

       printf("%d ",a[i]);

    }

    printf("\n");

}

void QuickSort(int a[],int start,int end)

{

    int i=start,j=end;

    //选中第一个的值为start

    int base=a[start];

    int temp;

    //递归的结束条件,如果i>=j;说明数组已经排好序了,直接返回

    if(i>=j)

    return ;

    while(i

    {

       //从后向前找出比基准值小的下标 [比基准值大就继续往前找]

       for(;j>i&&a[j]>=base;j--); 

       //从前向后找出比基准值大的小标  [比基准值小就继续往后找]

       for(;i

       //交换找到的值

       if(i

       {

       temp=a[i];

       a[i]=a[j];

       a[j]=temp;

       }

       //继续while循环

    }

    //i 的值就是最后的位置,将其与基准值进行

    a[start]=a[i];

    a[i]=base;

    QuickSort(a,start,i-1);

    QuickSort(a,i+1,end);

}

int main(void)

{

    int a[10]={3,5,7,6,1,-9,-8,79,100,0};

    print(a,10);

    QuickSort(a,0,9);

    print(a,10);

}

Java代码实现

public class QuickSort {

    public static void main(String[] args) {

        int[] a = {3, 5, 7, 6, 1, -9, -8, 79, 100, 0};

        print(a);

        QuickSort(a, 0, a.length - 1);

        print(a);

    }

 

    public static void QuickSort(int[] a, int start, int end) {

        int i=start,j=end;

        int base=a[start];

        int temp;

        if(i>=j)

            return;

        while (i

        {

            for(;j>i&&a[j]>=base;j--);

            for(;i

            if(i

            {

                temp=a[i];

                a[i]=a[j];

                a[j]=temp;

            }

        }

        a[start]=a[i];

        a[i]=base;

        QuickSort(a,start,i-1);

        QuickSort(a,i+1,end);

    }

 

    public static void print(int[] a) {

        for (int i : a) {

            System.out.print(i);

            System.out.print(" ");

        }

        System.out.println();

    }

}

 

3.hashmap了解吗,什么时候初始化

HashMap初始化的时间是在进行put操作的时候,调用resize方法

4.java的并发包有什么了解吗

https://www.cnblogs.com/cccw/p/5837448.html

Java.util.concurrent包

该包下面的类有

1.ConcurrentHashMap使用分段存储的方法

2.ReentrantLock底层使用volatile和AQS同步器进行实现

3.Condition接口,实现类例如ReentrantLock

4.ThreadPoolExcutor线程池

5.Future和FutureTask异步等待

6.CopyOnWriteArrayList底层实是ReentrantLock

5.原子类有了解吗,原理是什么

Atomic原子类

1. 原子类由CAS操作保持原子性,由volatile关键字保证可见性

2. 原子类可以粗略分为5类

a. 整型,长整型,布尔型,引用类型的原子类

AtomicInteger,AtomicLong,AtomicBoolean.AtomicReference

b. 整型数组,长整型数组,引用数组的原子类

AtomicIntegerArray,AtomicLongArrat,AtomicReferenceArray

c. 整型字段,长整型字段,引用字段更新的原子类

AtomicIntegerFieldUpdater,AtomicLongFieldUpdater,AtomicReferenceUpdater

d  解决ABA问题的原子类

AtomicMarkableReference,AtomicStampedReference

E jdk1.8新增的更高性能的原子累加器

LongAdder,DoubleAdder,LongAccumulator,DoubleAccumulator

6.CAS的好处和坏处是什么

好处:乐观锁,可以解决并发带来的不安全的问题

https://www.cnblogs.com/senlinyang/p/7875381.html

坏处:

1. ABA问题

线程1从内存位置V中取出A,这时候另一个线程2从内存中取出A并且线程2进行一些操作变成了B,然后线程2又将v位置的数据变成A,这时候线程1进行CAS操作发现内存中仍然是A,,然后线程1操作成功,尽管操作成功,但是仍然存在一些线程安全问题

比如A在栈顶,做了一些操作之后例如push一些数字,还把A放在栈顶,这时候就有可能增加一些东西,同时还可能减少一些东西

2. 循环开销时间大

自旋CAS(不成功就一直循环执行,直到成功)如果长时间不成功,那么会给cpu带来非常大的执行开销,如果jvm能支持处理的pause指令,那么效率会有一定的提升

Pause指令有两个作用:可以延迟流水线的执行指令,使得cpu不会消耗过多的执行资源,延迟的时间取决与具体实现的版本,第二可以避免退出循环的时候因为内存顺序冲突而引起的cpu流水线被清空

3只能保证一个共享变量的原子操作

当对多个锁进行操作时,CAS无法保证原子性,这个时候可以用锁,或者将两个两个变量合并成一个变量进行操作.比如有两个共享变量i=2,j=a,合并一下两个变量ij=2a,然后用cas来操作I,j,jdk提供了AtmoicReference类来保证引用对象之间的原子性

7.jvm的内存模型,详细说一下jvm的堆空间划分

内存模型:

JMM决定一个线程对共享变量的写吐何时对另一个线程可见。线程之间的共享内存在主内存中,每个线程还有一个私有的本地内存,本地内存中存储了用于读写的共享变量的副本

支撑java内存模型的基础原理

指令重排序

在执行程序时,编译器和cpu为了提高效率会对指令进行重排序,通过插入memory Barrier禁止重排序,为上一层提供可见性保证

  1. 编译器优化重排序:编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序
  2. 指令级并行的重排序:如果不存在数据依赖性,处理器可与改变语句对应机器指令的执行顺序
  3. 内存系统的重排序:处理器使用缓存和读写缓冲区,这使得加载和存储操作看上去可能是在乱序执行

Jvm堆的空间划分

https://blog.csdn.net/weixin_40160053/article/details/82841508

jdk1.8之前分为新生代老年代和永久代

jdk1.8之后将永久代分到栈中

新生代:所有new的对象都在堆里面,对象有限分配到Eden区,可以通过参数来指定Eden区和survivior的比例(复制算法中用到的,1:1空间划分太浪费)

老年代:大对象和长期存活的对象

永久代:包含元数据信息,如class,method的detail信息

New一个对象的过程:

8.jvm的gc算法,为什么要gc?full gc和minor gc的区别?分别都是什么时候触发

如果只创建对象,不回收对象,那么内存很快就会被耗尽,所以要进行GC

MinorGC触发条件,当新生代的Eden区满时,会触发minorGC

FullGC触发条件:

a  system.gc系统执行GC,但是不是必然执行(没有其他对象对其进行引用时才确定)

b.老年代空间不足

c方法区空间不足

d minor gc之后进入老年代的平均大小大于老年代的可用内存

e由Eden区的from space区域向tospace区域转换时,tospace区域的内存不足,且把该对象转向老年代之后,老年代的可用内存小于该对象大小

(一)Minor GC

Minor GC 是发生在新生代中的垃圾收集动作,所采用的是复制算法。 是发生在新生代中的垃圾收集动作,所采用的是复制算法。

当对象在 在 Eden 区 区和 和 From Survivor 区 区, ,(Survivor  区“To” 是空的), ,在经过一次 在经过一次 Minor

GC  后,Eden 区中所有存活的对象都会被复制到 区中所有存活的对象都会被复制到“ “To” ” ,而在“ “From” ” 区中,仍存活的

对象会根据他们的年龄值来决定去向。对象在 对象会根据他们的年龄值来决定去向。对象在 Survivor 次 区每熬过一次 Minor GC,就将 ,就将

对象的年龄 对象的年龄 + 1时 ,当对象的年龄达到某个值时 (  默认是 15 岁,可以通过参数 岁,可以通过参数-XX:MaxTenuringThreshold 定 来设定 )会被移动到年老代中 会被移动到年老代中 , 没有达到阈值的对象会被复制到“ “To” ” 区域 ,经过这次 经过这次 GC 后 后, ,Eden  区和 From 区已经被清空 区已经被清空 。 这个时候 ,“ “From” ”和“ “To” ” 会交换他们的角色 , 也就是新的“ “To” ”就是上次 就是上次 GC 前的 前的“ “From” ” , 新的“ “From” ”就是上次 就是上次 GC 前的 前的“ “To” 不管怎样, ,都会保证名为 都会保证名为 To 的 的 Survivor 区域是空的 区域是空的Minor GC会一直重复这样的过程,直到“ “To” ” 区被填满,“ “To” ” 区被填满之后,会将所有对象移动到年老代中。

(二)Full GC

Full GC 是 是 发生在老年代 的垃圾收集动作,所采用的是标记 标记-清除 清除或标记 或标记-整理 整理 算法 。

在 老年代里面的对象几乎都是在 Survivor , 区域中熬过来的, Full GC  发生的次数不

有 会有 Minor GC 次 那么频繁,并且做一次 Full GC 次 要比进行一次 Minor GC  的时间更长。

另外, , 标记- 清除算法收集垃圾的时候会产生许多的内存碎片 ( 间 即不连续的内存空间 )此后需要为较大的对象分配内存空间时,若无法找到足够的连续的内存空间,就会提前触发一次GC的收集动作。

 

 

你可能感兴趣的:(面经)