校招面试题5-------cvte/美团

1.static的在内存中的存放位置

程序在内存分区=代码区code+数据区(动态数据区(heap+stack)+静态数据区(data))
而内存分为四个区:stack segment,heap segment,data segment,code segment;
stack 区存放函数参数和局部变量;heap  区存放对象;data  区存放static 的变量或者字符串常量; code  区存放类中的方法;
因此,静态变量是存放在data区的 !(data区==静态数据区==方法区)(动态数据区=heap+stack) (数据区=动态+静态数据区)

9.对后台的优化有了解吗?比如负载均衡。

京东lb+服务器集群+nginx

1.进程间共享内存的方式有哪些?(8种)

无名管道,有名管道,高级管道,信号量,消息队列,共享内存,套接字。

5.HashMap高并发情况下会出现什么问题,(扩容的时候会出现问题,易导致程序死循环)

(当hashmap.size>capicity*load factor时,进行rehash,rehash的过程entry数组扩为原来的2倍,int index=hashcode&(length-1

entry元素重新排列啦,单线程环境下rehash没问题,多线程环境下两个线程同时rehash,就会出现环形链表,导致程序死循环

这一期我们来讲解高并发环境下,HashMap可能出现的致命问题。

                

                

                     

              

 

HashMap的容量是有限的。当经过多次元素插入,使得HashMap达到一定饱和度时,Key映射位置发生冲突的几率会逐渐提高。

这时候,HashMap需要扩展它的长度,也就是进行Resize。

 

                

 

影响发生Resize的因素有两个:

 

  • Capacity

HashMap的当前长度。上一期曾经说过,HashMap的长度是2的幂。

  • LoadFactor

HashMap负载因子,默认值为0.75f。

 

衡量HashMap是否进行Resize的条件如下:

 

HashMap.Size   >=  Capacity * LoadFactor

 

                

              

 

 

  • 扩容

创建一个新的Entry空数组,长度是原数组的2倍。

 

  • ReHash

遍历原Entry数组,把所有的Entry重新Hash到新数组。为什么要重新Hash呢?因为长度扩大以后,Hash的规则也随之改变。

 

让我们回顾一下Hash公式:

index =  HashCode(Key) &  (Length - 1) 

 

当原数组长度为8时,Hash运算是和111B做与运算;新数组长度为16,Hash运算是和1111B做与运算。Hash结果显然不同。

 

 

Resize前的HashMap:

 

              

Resize后的HashMap:

              

 

ReHash的Java代码如下:

 

              

                                          

 

注意:下面的内容十分烧脑,请小伙伴们坐稳扶好。

 

 

假设一个HashMap已经到了Resize的临界点。此时有两个线程A和B,在同一时刻对HashMap进行Put操作:

 

              

 

              

此时达到Resize条件,两个线程各自进行Rezie的第一步,也就是扩容:

              

这时候,两个线程都走到了ReHash的步骤。让我们回顾一下ReHash的代码:

              

 

假如此时线程B遍历到Entry3对象,刚执行完红框里的这行代码,线程就被挂起。对于线程B来说:

 

e = Entry3

next = Entry2

 

这时候线程A畅通无阻地进行着Rehash,当ReHash完成后,结果如下(图中的e和next,代表线程B的两个引用):

 

              

 

直到这一步,看起来没什么毛病。接下来线程B恢复,继续执行属于它自己的ReHash。线程B刚才的状态是:

 

 

e = Entry3

next = Entry2

 

              

当执行到上面这一行时,显然 i = 3,因为刚才线程A对于Entry3的hash结果也是3。

              

 

我们继续执行到这两行,Entry3放入了线程B的数组下标为3的位置,并且e指向了Entry2。此时e和next的指向如下:

 

e = Entry2

next = Entry2

 

整体情况如图所示:

 

              

 

 

接着是新一轮循环,又执行到红框内的代码行:

              

 

e = Entry2

next = Entry3

 

整体情况如图所示:

 

              

接下来执行下面的三行,用头插法把Entry2插入到了线程B的数组的头结点:

              

整体情况如图所示:

              

第三次循环开始,又执行到红框的代码:

              

 

e = Entry3

next = Entry3.next = null

 

最后一步,当我们执行下面这一行的时候,见证奇迹的时刻来临了:

 

              

 

newTable[i] = Entry2

e = Entry3

Entry2.next = Entry3

Entry3.next = Entry2

 

链表出现了环形!

 

整体情况如图所示:

 

              

此时,问题还没有直接产生。当调用Get查找一个不存在的Key,而这个Key的Hash结果恰好等于3的时候,由于位置3带有环形链表,所以程序将会进入死循环!

8.对于SQL慢查询的优化?

(主要是从查询语句和数据库表设计两个方面来考虑,查询语句方面可以增加索引,增加查询筛选的限制条件;数据库表设计的时候可以拆分表,设计得更细粒度。但是后来才发现面试官想要的就是查询大量数据的慢查询问题的优化。。。)
先使用explain等找到耗时的地方,然后针对性的优化。SQL语句的优化。应该是需要针对业务数据进行一些特殊的优化。比如数据分类等。如果无法改变数据库设计,那就只能优化SQL语句,改变数据库参数设置了。

MySQL 处理海量数据时的一些优化查询速度方法

查询速度慢的原因

1、没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷)

2、I/O 吞吐量小,形成了瓶颈效应。

3、没有创建计算列导致查询不优化。

4、内存不足

5、网络速度慢

6、查询出的数据量过大(可采用多次查询,其他的方法降低数据量)

7、锁或者死锁(这是查询慢最常见的问题,是程序设计的缺陷)

8、sp_lock,sp_who,活动的用户查看,原因是读写竞争资源。

9、返回了不必要的行和列 

10、查询语句不好,没有优化 

针对导致查询速度慢的原因,进行优化,比如内存太小,换个大内存,比如查询数据量太大,分多次查询。。。。。。。。。

9.用过哪些容器?(tomcat)对比过Tomcat与其他服务器的区别吗?比如nginx?

tomcat是servlet容器,J2EE定义了多个层次的服务,最初的tomcat只工作在servlet这层。 Nginx和Apache是web服务器,更往前的一层,最初是处理静态资源的。
Nginx出现的比较晚,所以解决了Apache的很多缺点,比较轻量级。由于Nginx的高性能,现在互联网公司一般把Nginx用做第七层的软件负载均衡,工作在tomcat前面,后面可能还有jboss, WebSphere, WebLogic等应用服务器。
nginx常用做静态内容服务和代理服务器,Tomcat能够动态的生成资源并返回到客户端。
Nginx 应该叫做「HTTP Server」;而 Tomcat 则是一个「Application Server」,或者更准确的来说,是一个「Servlet/JSP」应用的容器;
虽然Tomcat也可以认为是HTTP服务器,但通常它仍然会和Nginx配合在一起使用:
动静态资源分离——运用Nginx的反向代理功能分发请求:所有动态资源的请求交给Tomcat,而静态资源的请求(例如图片、视频、CSS、JavaScript文件等)则直接由Nginx返回到浏览器,这样能大大减轻Tomcat的压力。
负载均衡,当业务压力增大时,可能一个Tomcat的实例不足以处理,那么这时可以启动多个Tomcat实例进行水平扩展,而Nginx的负载均衡功能可以把请求通过算法分发到各个不同的实例进行处理。

在方法区,方法区用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等。

6.java中hashMap结构,处理冲突方法,还有啥方法,各个方法优缺点

用拉链发实现的hash表存储
 1.开放定址法,这种方法也称再散列法,其基本思想是:当关键字key的哈希地址p=H(key)出现冲突时,以p为基础,产生另一个哈希地址p1,如果p1仍然冲突,再以p为基础,产生另一个哈希地址p2,…,直到找出一个不冲突的哈希地址pi ,将相应元素存入其中。
2.再哈希法,这种方法是同时构造多个不同的哈希函数:
    Hi=RH1(key)  i=1,2,…,k
当哈希地址Hi=RH1(key)发生冲突时,再计算Hi=RH2(key)……,直到冲突不再产生。这种方法不易产生聚集,但增加了计算时间。
3.  链地址法,这种方法的基本思想是将所有哈希地址为i的元素构成一个称为同义词链的单链表,并将单链表的头指针存在哈希表的第i个单元中,因而查找、插入和删除主要在同义词链中进行。链地址法适用于经常进行插入和删除的情况。
4.建立公共溢出区,这种方法的基本思想是:将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表

12.volatile实现原理
它在多处理器开发中保证了共享变量的“可见性”。可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。
将当前处理器缓存行的数据会写回到系统内存。
这个写回内存的操作会引起在其他CPU里缓存了该内存地址的数据无效。
处理器为了提高处理速度,不直接和内存进行通讯,而是先将系统内存的数据读到内部缓存(L1,L2或其他)后再进行操作,但操作完之后不知道何时会 写到内存,如果对声明了Volatile变量进行写操作,JVM就会向处理器发送一条Lock前缀的指令,将这个变量所在缓存行的数据写回到系统内存。但是就算写回到内存,如果其他处理器缓存的值还是旧的,再执行计算操作就会有问题,所以在多处理器下,为了保证各个处理器的缓存是一致的,就会实现缓存一致性协议,每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,当处理器要对这个数据进行修改操作的时候,会强制重新从系统内存里把数据读到处理器缓存里。
缓存一致性机制会阻止同时修改被两个以上处理器缓存的内存区域数据。一个处理器的缓存回写到内存会导致其他处理器的缓存无效

14.boolean占几个字节

因为jvm没有boolean类型,所以如果jvm实现规范的话,那么如果是boolean类型的数组的话,每个boolean类型(按照byte数组存储)占用1bytes,否者按照int类型存储,占用4bytes.得出boolean类型占了单独使用是4个字节,在数组中又是1个字节

为什么要问这个问题,首先在Java中定义的八种基本数据类型中,除了其它七种类型都有明确的内存占用字节数外,就boolean类型没有给出具体的占用字节数,因为对虚拟机来说根本就不存在 boolean 这个类型,boolean类型在编译后会使用其他数据类型来表示,那boolean类型究竟占用多少个字节?带着疑问,随便网上一搜,答案五花八门,基本有以下几种:


1、1个bit

理由是boolean类型的值只有true和false两种逻辑值,在编译后会使用1和0来表示,这两个数在内存中只需要1位(bit)即可存储,位是计算机最小的存储单位。

2、1个字节

理由是虽然编译后1和0只需占用1位空间,但计算机处理数据的最小单位是1个字节,1个字节等于8位,实际存储的空间是:用1个字节的最低位存储,其他7位用0填补,如果值是true的话则存储的二进制为:0000 0001,如果是false的话则存储的二进制为:0000 0000。

3、4个字节

理由来源是《Java虚拟机规范》一书中的描述:“虽然定义了boolean这种数据类型,但是只对它提供了非常有限的支持。在Java虚拟机中没有任何供boolean值专用的字节码指令,Java语言表达式所操作的boolean值,在编译之后都使用Java虚拟机中的int数据类型来代替,而boolean数组将会被编码成Java虚拟机的byte数组,每个元素boolean元素占8位”。这样我们可以得出boolean类型占了单独使用是4个字节(boolean-int),在数组(boolean-byte)中又是1个字节。

显然第三条是更准确的说法,那虚拟机为什么要用int来代替boolean呢?为什么不用byte或short,这样不是更节省内存空间吗。大多数人都会很自然的这样去想,我同样也有这个疑问,经过查阅资料发现,使用int的原因是,对于当下32位的处理器(CPU)来说,一次处理数据是32位(这里不是指的是32/64位系统,而是指CPU硬件层面),具有高效存取的特点。

最后的总结:

根据http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html官方文档的描述:

boolean: The boolean data type has only two possible values: true and false. Use this data type for simple flags that track true/false conditions. This data type represents one bit of information, but its "size" isn't something that's precisely defined.

布尔类型:布尔数据类型只有两个可能的值:真和假。使用此数据类型为跟踪真/假条件的简单标记。这种数据类型就表示这一点信息,但是它的“大小”并不是精确定义的。

可以看出,boolean类型没有给出精确的定义,《Java虚拟机规范》给出了4个字节,和boolean数组1个字节的定义,具体还要看虚拟机实现是否按照规范来,所以1个字节、4个字节都是有可能的。这其实是运算效率和存储空间之间的博弈,两者都非常的重要

4.同步方法。
(1)synchronized关键字修饰的语句块,synchronized只能标记非抽象的方法,不能标识成员变量。synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类。---硬件实现
(2)使用特殊域变量(Volatile)实现线程同步-------硬件实现
a.volatile关键字为域变量的访问提供了一种免锁机制
b.使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新
c.因此每次使用该域就要重新计算,而不是使用寄存器中的值
d.volatile不会提供任何原子操作,它也不能用来修饰final类型的变量
(3)ReentrantLock类是可重入、互斥、实现了Lock接口的锁;
(4)使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变 量副本,而不会对其他线程产生影

11.谈一谈对volatile理解,这个问题很常见,答出要点:
可见性、防止指令重排即可。
在并发编程中,我们通常会遇到以下三个问题:原子性问题,可见性问题,有序性问题。
只有简单的读取、赋值(而且必须是将数字赋值给某个变量,变量之间的相互赋值不是原子操作)才是原子操作。
1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的
  2)禁止进行指令重排序。
3)volatile也无法保证对变量的任何操作都是原子性的。
原理:
“观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令”
  lock前缀指令实际上相当于一个内存屏障(也成内存栅栏),内存屏障会提供3个功能:
  1)它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;
  2)它会强制将对缓存的修改操作立即写入主存;
  3)如果是写操作,它会导致其他CPU中对应的缓存行无效。

10.HashMap如果有很多相同key,后面的链很长的话,你会怎么优化?或者你会用什么数据结构来存储?

红黑树

6.Out of MemoryError 产生的原因是什么,具体怎么去调优,以及理解那几个参数的含义 -Xms, -Xmx ,-Xmn, -XX:PermSize
内存泄露是指程序在运行过程中动态申请的内存空间不再使用后没有及时释放,从而很可能导致应用程序内存无线增长。
内存溢出即用户在对其数据缓冲区操作时,超过了其缓冲区的边界;尤其是对缓冲区写操作时,缓冲区的溢出很可能导致程序的异常。
原因:
1.内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
2.集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
3.代码中存在死循环或循环产生过多重复的对象实体;
4.启动参数内存值设定的过小;
调优:
1)检查代码中是否有死循环或递归调用。
2)检查是否有大循环重复产生新对象实体。
3)检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出
4 )检查List、MAP等集合对象是否有使用完后,未清除的问题
5)应用服务器提示错误的解决: 把启动参数内存值设置足够大
-Xms, -Xmx 最小,最大堆大小;-Xmn新生代大小;-XX:PermSize设置永久代大小;
 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值