Привет всем, так как я пишу ошибки и исправляю ошибки каждый день, сегодня я представляю вам аварию, которая была исправлена всего несколько дней назад. Должен признать, там, где я нахожусь, всегда так много ошибок.
причина
Начало истории произошло несколько дней назад. Была функция экспорта, которая не очень часто использовалась. Пользователи сообщали, что независимо от условий был только один кусок экспортируемых данных. данные. (Эта проблема была исправлена, поэтому журнал Kibana на тот момент не может быть найден). Поэтому я отложил свою работу и вставил ее, чтобы посмотреть на эту проблему.
анализировать
Судя по описанию проблемы, это возможно только в следующих ситуациях:
- На основе критериев поиска запрашивается только одна запись.
- Проверить данные, связанные с бизнес-процессами, ведущими к конечному результату, можно только одним.
- После логической обработки компонента экспорта файлов результат один.
Не по теме
После написания этого я вдруг подумал о классическом вопросе интервью, анализе сценария потери сообщения MQ. Хахаха, на самом деле, это грубо проанализировано с нескольких точек зрения. (есть возможность написать статью на MQ)
Не по теме
Итак, давайте проанализируем их один за другим:
- Найдите SQL соответствующего бизнеса и соответствующие параметры, запрос доступен и имеется более одной части данных, поэтому первый случай можно исключить.
- В промежуточном бизнесе есть связанные разрешения и конфиденциальность данных.После того, как все они выпущены, остается только одна часть данных.
- Когда компонент экспорта файлов получает данные, распечатанный журнал также показывает, что существует только один журнал, а это означает, что должна быть проблема с логикой промежуточного связанного бизнеса.
Так как этот код написан целым методом, Артасу сложно его проверить, поэтому приходится пошагово ставить лог для проверки. (Итак, если это большой кусок логики, рекомендуется разбить его на дуоге подметоды. Во-первых, при написании идея ясна, и есть модульная концепция. Что касается повторного использования методов, я буду не упоминать больше.Основная операция; Во-вторых, как только возникает проблема, будет удобнее устранять неполадки, исходя из опыта).
Это заканчивается циклом for.
код
Без дальнейших церемоний, давайте перейдем непосредственно к коду. Как мы все знаем, я всегда был человеком, который защищает код компании, поэтому я должен смоделировать его для всех здесь. Из ситуации проблемы запись экспортированного объекта пуста
import com.google.common.collect.Lists;
import java.util.List;
public class Test {
public static void main(String[] args) {
// 获取Customer数据,这里就简单模拟
List<Customer> customerList = Lists.newArrayList(new Customer("Java"), new Customer("Showyool"), new Customer("Soga"));
int index = 0;
String[][] exportData = new String[customerList.size()][2];
for (Customer customer : customerList) {
exportData[index][0] = String.valueOf(index);
exportData[index][1] = customer.getName();
index = index++;
}
System.out.println(JSON.toJSONString(exportData));
}
}
class Customer {
public Customer(String name) {
this.name = name;
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Этот код, похоже, не что иное, как преобразование коллекции Customer в двумерный массив строк. Но вывод показывает:Это соответствует тому, что мы сказали, запросов много, а выход только один.
Присмотревшись, мы можем обнаружить, что отображение выходных данных является последним, то есть каждый раз, когда просматривается коллекция Customer, последняя перезаписывает первую, то есть индекс этого индекса никогда не менялся, всегда было 0.
моделирование
Таким образом, наш автоинкремент действительно немного проблематичен, поэтому давайте просто напишем модель
public class Test2 {
public static void main(String[] args) {
int index = 3;
index = index++;
System.out.println(index);
}
}
Мы упрощаем приведенную выше бизнес-логику в такой модели, тогда результат не удивителен 3.
объяснять
Итак, давайте запустим javap, чтобы увидеть, как интерпретируется байт-код JVM:
javap -c Test2
Compiled from "Test2.java"
public class com.showyool.blog_4.Test2 {
public com.showyool.blog_4.Test2();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_3
1: istore_1
2: iload_1
3: iinc 1, 1
6: istore_1
7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
10: iload_1
11: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
14: return
}
Здесь я кратко расскажу об инструкциях байт-кода JVM здесь (позже будет возможность написать статьи подробно)
Прежде всего, нам нужно знать, что здесь есть два понятия, стек операндов и таблица локальных переменных, которые представляют собой некие структуры данных в кадре стека в стеке виртуальной машины, как показано на рисунке:
Затем давайте взглянем на приведенную выше команду:
0: iconst_3 (сначала поместить в стек константу 3)
продлить его
Таким образом, мы обнаружили, что проблема на самом деле заключается в последнем шаге: после выполнения операции значение, записанное в исходном стеке, переназначается переменной, перезаписывая ее. Если мы напишем это:
public class Test2 {
public static void main(String[] args) {
int index = 3;
index++;
System.out.println(index);
}
}
Compiled from "Test2.java"
public class com.showyool.blog_4.Test2 {
public com.showyool.blog_4.Test2();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_3
1: istore_1
2: iinc 1, 1
5: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
8: iload_1
9: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
12: return
}
Можно обнаружить, что на последнем шаге нет istore_1, поэтому после iinc значение index становится равным 4, как мы и ожидали.
Есть и другая ситуация, давайте разберемся:
public class Test2 {
public static void main(String[] args) {
int index = 3;
index = index + 2;
System.out.println(index);
}
}
Compiled from "Test2.java"
public class com.showyool.blog_4.Test2 {
public com.showyool.blog_4.Test2();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_3
1: istore_1
2: iload_1
3: iconst_2
4: iadd
5: istore_1
6: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
9: iload_1
10: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
13: return
}
0: iconst_3 (сначала поместить в стек константу 3)
1: istore_1 (операция pop, присвоить значение первому параметру, то есть присвоить 3 индексу)
2: iload_1 (запихнуть в стек значение первого параметра, то есть запихнуть в стек 3, а значение на вершине стека равно 3)
3: iconst_2 (поместите константу 2 в стек, значение наверху стека равно 2, а 2 выше 3)
4: iadd (сложить два числа наверху стека и поместить результат в стек. 2+3=5, значение наверху стека равно 5)
5: istore_1 (операция pop, присвоить значение первому параметру, то есть присвоить 5 индексу)
Увидев здесь публику, у вас должно возникнуть такое сомнение, почему операция сложения iadd здесь влияет на данные в стеке, а упомянутый ранее iinc в стеке не работает? Что ж, можем посмотреть спецификацию виртуальной машины JVM, она описана так:
Инструкция iinc выполняет операцию автоматического увеличения заданной локальной переменной.Эта инструкция — одна из немногих инструкций, которые вообще не изменяют стек операндов во время выполнения. Он принимает два операнда:
Позиция 1-й таблицы локальных переменных, 2-й бит аккумулятора. Например, обычный i++ сгенерирует эту инструкцию
Видя это, мы знаем, что проблем с копированием после общей операции сложения нет, но после использования i++ число на вершине стека в этот момент все еще является старым значением. вернется к исходному старому значению, т.к. не изменяет данные в стеке. Так что предыдущую ошибку можно исправить только автоинкрементом и никаким присваиванием.
Наконец
Спасибо, что вы здесь, выше весь процесс моей борьбы с этой ошибкой. Хотя это всего лишь небольшая ошибка, ее стоит изучить и обдумать. В дальнейшем я продолжу делиться найденными ошибками и знаниями.Если моя статья будет вам полезна, надеюсь, вы, ребята, , Еще раз спасибо за вашу поддержку!
Вот также мой адрес Github:GitHub.com/showyoOL/Согласно…