Оригинал: Miss Sister Taste (идентификатор публичной учетной записи WeChat: xjjdog), добро пожаловать, пожалуйста, сохраните источник для перепечатки.
Обработка чисел — это основа языка. Если даже 1+1 становится ненадежным, вся программа становится ненадежной.
Рассмотрим этот фрагмент кода:
Integer a = 1;
System.out.println(a);
Integer b = 2;
System.out.println( a.intValue() == b.intValue() );
System.out.println(a.equals(b));
Результат выполнения оказался таким:
-996
true
true
В это время вы осмеливаетесь продолжать писать код?
Почему это происходит?
Проще говоря, мы что-то изменили с помощью рефлексии.
Следующий код изменит логику выполнения некоторых базовых операций, и конечно он относится к одной из категорий зарытых ям. Давайте сначала посмотрим на его поведение.
public class StaticBlock {
static {
try {
Class<?> cls = Integer.class.getDeclaredClasses()[0];
Field f = cls.getDeclaredField("cache");
f.setAccessible(true);
Integer[] cache = ((Integer[]) f.get(cls));
for (int i = 0; i < cache.length; i++) {
cache[i] = -996;
}
} catch (Exception e) {
e.printStackTrace();
//silence
}
}
}
Программа использует рефлексию, модифицированнуюIntegerсерединаcacheСодержимое переменной приводит к тому, что число в ней становится фиксированным значением. Здесь мы используем-996, то есть никогда не будет 996.
Пока вы найдете способ запустить этот код, класс-оболочка Java Integer бесполезен.
Мы можем это сделать, ключ лежит в переменной кеша.
цифровой кэш
В Java есть 8 основных типов.Ввиду объектно-ориентированных характеристик Java им также соответствует 8 типов упаковки, таких как int и Integer.Значение типа упаковки может быть нулевым, и во многих случаях они могут быть присвоены друг другу.
Судя по следующему небольшому коду, его работа претерпела множественные упаковки и распаковки.
public Integer cal() {
Integer a = 1000;
int b = a * 10;
return b;
}
Давайте посмотрим на это на уровне байт-кода.
public java.lang.Integer read();
descriptor: ()Ljava/lang/Integer;
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=1
0: sipush 1000
3: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
6: astore_1
7: aload_1
8: invokevirtual #3 // Method java/lang/Integer.intValue:()I
11: bipush 10
13: imul
14: istore_2
15: iload_2
16: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
19: areturn
Можно видеть, что такая простая операция многократно включает такие методы, как valueOf и intValue, что указывает на то, что процесс ее вычисления менее эффективен, чем простые числовые операции.
Среди них метод valueOf используется для переноса обычных чисел в Integer, и мы отслеживаем его методы.
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
Чтобы повысить эффективность преобразования, соответствующая связь между i и Integer кэшируется внутри Integer! Таким образом, в следующий раз, когда вы используете его, вы можете найти его напрямую. Переменная кэша — это место для хранения этой промежуточной информации. Если бы мы изменили его с помощью отражения, Integer вел бы себя хаотично!
Более
IntegerCache, который кэширует объекты Integer между низким и высоким уровнем, может быть доступен через-XX:AutoBoxCacheMaxдля изменения верхнего предела.
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
Интересно, что у Long тоже есть такой Cache, но его верхняя и нижняя границы фиксированы, как у Byte и Short.
static final Long cache[] = new Long[-(-128) + 127 + 1];S
Double и Float убогие, можно только напрямую новый, а такое кеширование делать нельзя.
В целом, Integer совершенно особенный. Следующий код, даже если мы не изменим магию отражения, его результат все еще не определен.
Integer n1 = 123;
Integer n2 = 123;
Integer n3 = 128;
Integer n4 = 128;
System.out.println(n1 == n2);
System.out.println(n3 == n4);
Это потому, что при нормальных обстоятельствах он выводит true, false; и когда мы используемAutoBoxCacheMaxУвеличьте его верхний предел, он выведет true, true. Конечно же, чтобы сравнивать объекты друг с другом, надежнее использовать равные.
End
Глядя на эту яму по грудь, мои чувства действительно неописуемы. С общей точки зрения этого кода, если провести нормальный ревью, увидеть проблему несложно, но всегда есть кейсы.
Если этот код выложить в сеть, даже если симпатичный одноклассник случайно отправит его на склад во время практики, последствия будут разрушительными. Цель этого кода относительно проста, но если мы немного усложним логику модификации кэш-массива, изменение значения одной переменной будет срабатывать только при определенных условиях, что является фатальным.
В конце концов, даже сонар не может быть просканирован, а в jdk все еще есть куча частных переменных, которые ждут, чтобы мы их исследовали!
Об авторе:Мисс сестра вкус(xjjdog), публичная учетная запись, которая не позволяет программистам идти в обход. Сосредоточьтесь на инфраструктуре и Linux. Десять лет архитектуры, десятки миллиардов ежедневного трафика, обсуждение с вами мира высокой параллелизма, дающие вам другой вкус. Мой личный WeChat xjjdog0, добро пожаловать в друзья для дальнейшего общения.