面试大杂烩

面试大杂烩汇总

对象头和synchronize

在JVM中,对象在内存中除了本身的数据外还会有个对象头,对于普通对象而言,其对象头中有两类信息:mark word和类型指针。另外对于数组而言还会有一份记录数组长度的数据。

类型指针是指向该对象所属类对象的指针,mark word用于存储对象的HashCode、GC分代年龄、锁状态等信息。在32位系统上mark word长度为32bit,64位系统上长度为64bit。为了能在有限的空间里存储下更多的数据,其存储格式是不固定的,在32位系统上各状态的格式如下:

可以看到锁信息也是存在于对象的mark word中的。当对象状态为偏向锁(biasable)时,mark word存储的是偏向的线程ID;当状态为轻量级锁(lightweight locked)时,mark word存储的是指向线程栈中Lock Record的指针;当状态为重量级锁(inflated)时,为指向堆中的monitor对象的指针

轻量级锁

线程在执行同步块之前,JVM会先在当前的线程的栈帧中创建一个Lock Record,其包括一个用于存储对象头中的 mark word(官方称之为Displaced Mark Word)以及一个指向对象的指针。下图右边的部分就是一个Lock Record。

  • 加锁过程

  • 1.在线程栈中创建一个Lock Record,将其obj(即上图的Object reference)字段指向锁对象。

  • 2.直接通过CAS指令将Lock Record的地址存储在对象头的mark word中,如果对象处于无锁状态则修改成功,代表该线程获得了轻量级锁。如果失败,进入到步骤3。

  • 3.如果是当前线程已经持有该锁了,代表这是一次锁重入。设置Lock Record第一部分(Displaced Mark Word)为null,起到了一个重入计数器的作用。然后结束。

  • 4.走到这一步说明发生了竞争,需要膨胀为重量级锁。

  • 解锁过程
  • 1.遍历线程栈,找到所有obj字段等于当前锁对象的Lock Record。

  • 2.如果Lock Record的Displaced Mark Word为null,代表这是一次重入,将obj设置为null后continue。

  • 3.如果Lock Record的Displaced Mark Word不为null,则利用CAS指令将对象头的mark word恢复成为Displaced Mark Word。如果成功,则continue,否则膨胀为重量级锁。

偏向锁

JDK1.6中为了提高一个对象在一段很长的时间内都只被一个线程用做锁对象场景下的性能,引入了偏向锁,在第一次获得锁时,会有一个CAS操作,之后该线程再获取锁,只会执行几个简单的命令,而不是开销相对较大的CAS命令。

面试:—-利用solr实现商品的搜索功能

有一个1G的文件,每一行是一个词,词的大小不超过16字节,内存限制大小是1M。返回频数最高的100个词

由上面那两个例题,分而治之 + hash统计 + 堆/快速排序这个套路再多多验证下。此题又是文件很大,又是内存受限,无非还是

  • 分而治之/hash映射 顺序读文件中,对于每个词x,取hash(x)%5000,然后按照该值存到5000个小文件(记为x0,x1,…x4999)中。这样每个文件大概是200k。 如果其中的有的文件超过了1M,还可以按照类似的方法继续下分,直到分解得到的小文件都不超过1M

  • HashMap统计 对每个小文件,采用trie树/HashMap等统计每个文件中出现的词以及相应的频率

  • 堆/归并排 取出出现频率最大的100个词(可以用含100个结点的最小堆)后,再把100个词及相应的频率存入文件,这样又得到了5000个文件。最后就是把这5000个文件进行归并(类似于归并排序)的过程了。

    海量数据分布在10台电脑中,想个办法高效统计出这批数据的TOP10,如果每个数据元素只出现一次,而且只出现在某一台机器中,那么可以采取以下步骤统计出现次数TOP10的数据元素:

  • 堆排序 在每台电脑上求出TOP10,可以采用包含10个元素的堆完成(TOP10小,用最大堆,TOP10大,用最小堆,比如求TOP10大,我们首先取前10个元素调整成最小堆,如果发现,然后扫描后面的数据,并与堆顶元素比较,如果比堆顶元素大,那么用该元素替换堆顶,然后再调整为最小堆。最后堆中的元素就是TOP10大)。

  • 求出每台电脑上的TOP10后,然后把这100台电脑上的TOP10组合起来,共1000个数据,再利用上面类似的方法求出TOP10就可以了。

如果同一个元素重复出现在不同的电脑中呢

这个时候,你可以有两种方法

遍历所有数据,重新hash取模,使同一个元素只出现在单独的一台电脑中,然后采用上面所说的方法,统计每台电脑中各个元素的出现次数找出TOP10,继而组合100台电脑上的TOP10,找出最终的TOP10

暴力求解:直接统计每台电脑中各个元素的出现次数,然后把同一个元素在不同机器中的出现次数相加,最终从所有数据中找出TOP10

第6章redis使用场景(了解)

1、取最新N个数据的操作

比如典型的取你网站的最新文章,通过下面方式,我们可以将最新的5000条评论的ID放在Redis的List集合中,并将超出集合部分从数据库获取

  • (1)使用LPUSH latest.comments命令,向list集合中插入数据
  • (2)插入完成后再用LTRIM latest.comments05000命令使其永远只保存最近5000个ID
  • (3)然后我们在客户端获取某一页评论时可以用下面的逻辑(伪代码)
    伪代码

    FUNCTION get_latest_comments(start,num_items):id_list=redis.lrange(”latest.comments”,start,start+num_items-1) IFid_list.length< num_items id_list=SQL_DB(”SELECT…ORDER BY time LIMIT..…”) END RETURN id_list END

如果你还有不同的筛选维度,比如某个分类的最新N条,那么你可以再建一个按此分类的List,只存ID的话,Redis是非常高效的。

2、排行榜应用,取TOPN操作

这个需求与上面需求的不同之处在于,前面操作以时间为权重,这个是以某 个条件为权重,比如按顶的次数排序,这时候就需要我们的sorted set出 马了,将你要排序的值设置成sorted set的score,将具体的数据设置成 相应的value,每次只需要执行一条ZADD命令即可。

3、需要精准设定过期时间的应用

比如你可以把上面说到的sorted set的score值设置成过期时间的时间 戳,那么就可以简单地通过过期时间排序,定时清除过期数据了,不仅是清 除Redis中的过期数据,你完全可以把Redis里这个过期时间当成是对数据 库中数据的索引,用Redis来找出哪些数据需要过期删除,然后再精准地从 数据库中删除相应的记录。

4、计数器应用

Redis的命令都是原子性的,你可以轻松地利用INCR,DECR命令来构建计数 器系统。

5、Uniq操作,获取某段时间所有数据排重值

这个使用Redis的set数据结构最合适了,只需要不断地将数据往set中扔就 行了,set意为集合,所以会自动排重。

6、实时系统,反垃圾系统

通过上面说到的set功能,你可以知道一个终端用户是否进行了某个操作, 可以找到其操作的集合并进行分析统计对比等。没有做不到,只有想不到。

7、Pub/Sub构建实时消息系统

Redis的Pub/Sub系统可以构建实时的消息系统,比如很多用Pub/Sub构建 的实时聊天系统的例子。

8、构建队列系统

使用list可以构建队列系统,使用sorted set甚至可以构建有优先级的队列系统。

消息队列面试相关

redis

12、MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据?

redis内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。

13、Redis有哪些适合的场景?

  • (1)、会话缓存(Session Cache)最常用的一种使用Redis的情景是会话缓存(session cache)。用Redis缓存会话比其他存储(如Memcached)的优势在于:Redis提供持久化。当维护一个不是严格要求一致性的缓存时,如果用户的购物车信息全部丢失,大部分人都会不高兴的,现在,他们还会这样吗?幸运的是,随着 Redis 这些年的改进,很容易找到怎么恰当的使用Redis来缓存会话的文档。甚至广为人知的商业平台Magento也提供Redis的插件。
  • (2)、全页缓存(FPC)除基本的会话token之外,Redis还提供很简便的FPC平台。回到一致性问题,即使重启了Redis实例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降,这是一个极大改进,类似PHP本地FPC。再次以Magento为例,Magento提供一个插件来使用Redis作为全页缓存后端。此外,对WordPress的用户来说,Pantheon有一个非常好的插件 wp-redis,这个插件能帮助你以最快速度加载你曾浏览过的页面。
  • (3)、队列Reids在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得Redis能作为一个很好的消息队列平台来使用。Redis作为队列使用的操作,就类似于本地程序语言(如Python)对 list 的 push/pop 操作。如果你快速的在Google中搜索“Redis queues”,你马上就能找到大量的开源项目,这些项目的目的就是利用Redis创建非常好的后端工具,以满足各种队列需求。例如,Celery有一个后台就是使用Redis作为broker,你可以从这里去查看。
  • (4),排行榜/计数器Redis在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(Sorted Set)也使得我们在执行这些操作的时候变的非常简单,Redis只是正好提供了这两种数据结构。所以,我们要从排序集合中获取到排名最靠前的10个用户–我们称之为“user_scores”,我们只需要像下面一样执行即可:当然,这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数,你需要这样执行:ZRANGE user_scores 0 10 WITHSCORESAgora Games就是一个很好的例子,用Ruby实现的,它的排行榜就是使用Redis来存储数据的,你可以在这里看到。
  • (5)、发布/订阅最后(但肯定不是最不重要的)是Redis的发布/订阅功能。发布/订阅的使用场景确实非常多。我已看见人们在社交网络连接中使用,还可作为基于发布/订阅的脚本触发器,甚至用Redis的发布/订阅功能来建立聊天系统!(不,这是真的,你可以去核实)。

Redis中的管道有什么用?

一次请求/响应服务器能实现处理新的请求即使旧的请求还未被响应。这样就可以将多个命令发送到服务器,而不用等待回复,最后在一个步骤中读取该答复。这就是管道(pipelining),是一种几十年来广泛使用的技术。例如许多POP3协议已经实现支持这个功能,大大加快了从服务器下载新邮件的过程。

怎么理解Redis事务?

事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。

Redis如何做内存优化?

尽可能使用散列表(hashes),散列表(是说散列表里面存储的数少)使用的内存非常小,所以你应该尽可能的将你的数据模型抽象到一个散列表里面。比如你的web系统中有一个用户对象,不要为这个用户的名称,姓氏,邮箱,密码设置单独的key,而是应该把这个用户的所有信息存储到一张散列表里面.

Redis事务相关的命令有哪几个?

MULTI、EXEC、DISCARD、WATCH

Redis的内存占用情况怎么样?

给你举个例子: 100万个键值对(键是0到999999值是字符串“hello world”)在我的32位的Mac笔记本上 用了100MB。同样的数据放到一个key里只需要16MB, 这是因为键值有一个很大的开销。 在Memcached上执行也是类似的结果,但是相对Redis的开销要小一点点,因为Redis会记录类型信息引用计数等等。当然,大键值对时两者的比例要好很多。64位的系统比32位的需要更多的内存开销,尤其是键值对都较小时,这是因为64位的系统里指针占用了8个字节。 但是,当然,64位系统支持更大的内存,所以为了运行大型的Redis服务器或多或少的需要使用64位的系统。

都有哪些办法可以降低Redis的内存使用情况呢?

如果你使用的是32位的Redis实例,可以好好利用Hash,list,sorted set,set等集合类型数据,因为通常情况下很多小的Key-Value可以用更紧凑的方式存放到一起。

Redis是单线程的,如何提高多核CPU的利用率?

可以在同一个服务器部署多个Redis的实例,并把他们当作不同的服务器来使用,在某些时候,无论如何一个服务器是不够的, 所以,如果你想使用多个CPU,你可以考虑一下分片(shard)。

Redis常见性能问题和解决方案?

  • (1) Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件
  • (2) 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次
  • (3) 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内
  • (4) 尽量避免在压力很大的主库上增加从库
  • (5) 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3…这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。

    Redis提供了哪几种持久化方式?

  • RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储.
  • AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾.Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大.如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式.你也可以同时开启两种持久化方式, 在这种情况下, 当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整.最重要的事情是了解RDB和AOF持久化方式的不同,让我们以RDB持久化方式开始。

    如何选择合适的持久化方式?

    一般来说, 如果想达到足以媲美PostgreSQL的数据安全性, 你应该同时使用两种持久化功能。如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失,那么你可以只使用RDB持久化。有很多用户都只使用AOF持久化,但并不推荐这种方式:因为定时生成RDB快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比AOF恢复的速度要快,除此之外, 使用RDB还可以避免之前提到的AOF程序的bug。

用java代码实现守护进程

思路:

实际上就是执行某一段程序(不停的循环执行),每隔5秒更新job.log文件
代码如下:

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Date;
 
 
public class Program {
 
/**
 * @param args
 */
public static void main(String[] args) {
    File f = new File("/home/xieping/job.log");
    if (!f.exists()) {
        try {
            f.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    while (true) {
        try {
            BufferedWriter output = new BufferedWriter(new FileWriter(f));
            output.write(new Date().toString());
            output.close();
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        try {
            Thread.sleep(1000 * 5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
  }
} 

打包,我是用eclipse打包的,导出个jar包就行了。 把文件放到/home/xieping/目录下。文件名 job.jar。 开始设置为守护进程了!!

	#java -jar job.jar &  

简单吧,就加一个&符号。可以查看下

	#ps aux|grep job

看到了,果然启动了。

	#more /home/xieping/job.log

文件的时间也一直换。

注意,在开多线程的情况下,进程可能无法终止。
#killall java 或者 #kill 进程ID 无效。需要强制终止。 #kill -9 进程ID。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章